From 1caa72f162326d2c8bba5061b59829278f3f2344 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 14 Feb 2026 03:03:32 +0000 Subject: [PATCH 1/5] Fix obsolete GetRdfTypes usage in samples Replaced obsolete `GetRdfTypes()[0]` calls with `GetTypes().First()` in `ETMSample.cs` and `EWMSample.cs`. This eliminates `CS0618` warnings related to `QmResource.GetRdfTypes` and `ChangeRequest.GetRdfTypes`. Co-authored-by: berezovskyi <64734+berezovskyi@users.noreply.github.com> --- OSLC4Net_SDK/Examples/OSLC4Net.Client.Samples/ETMSample.cs | 2 +- OSLC4Net_SDK/Examples/OSLC4Net.Client.Samples/EWMSample.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/OSLC4Net_SDK/Examples/OSLC4Net.Client.Samples/ETMSample.cs b/OSLC4Net_SDK/Examples/OSLC4Net.Client.Samples/ETMSample.cs index 2ed77f2e..ef76708b 100644 --- a/OSLC4Net_SDK/Examples/OSLC4Net.Client.Samples/ETMSample.cs +++ b/OSLC4Net_SDK/Examples/OSLC4Net.Client.Samples/ETMSample.cs @@ -159,7 +159,7 @@ async Task RunAsync(string webContextUrl, string user, string passwd, string pro //Get the Creation Factory URL for test cases so that we can create a test case String testcaseCreation = await client.LookupCreationFactoryAsync( serviceProviderUrl, OSLCConstants.OSLC_QM_V2, - testcase.GetRdfTypes()[0].ToString()).ConfigureAwait(false); + testcase.GetTypes().First().ToString()).ConfigureAwait(false); //Create the test case HttpResponseMessage creationResponse = await client.CreateResourceRawAsync( diff --git a/OSLC4Net_SDK/Examples/OSLC4Net.Client.Samples/EWMSample.cs b/OSLC4Net_SDK/Examples/OSLC4Net.Client.Samples/EWMSample.cs index ba731c93..11d3b603 100644 --- a/OSLC4Net_SDK/Examples/OSLC4Net.Client.Samples/EWMSample.cs +++ b/OSLC4Net_SDK/Examples/OSLC4Net.Client.Samples/EWMSample.cs @@ -195,7 +195,7 @@ public async Task RunScenarioAsync(string webContextUrl, string user, string pas //Get the Creation Factory URL for change requests so that we can create one String changeRequestCreation = await client.LookupCreationFactoryAsync( serviceProviderUrl, OSLCConstants.OSLC_CM_V2, - changeRequest.GetRdfTypes()[0].ToString()).ConfigureAwait(false); + changeRequest.GetTypes().First().ToString()).ConfigureAwait(false); //Create the change request HttpResponseMessage creationResponse = await client.CreateResourceRawAsync( @@ -271,7 +271,7 @@ protected override void PrintResourceInfo(ChangeRequest cr) string creationFactoryUrl = await client.LookupCreationFactoryAsync( serviceProviderUrl, OSLCConstants.OSLC_CM_V2, - changeRequest.GetRdfTypes()[0].ToString()).ConfigureAwait(false); + changeRequest.GetTypes().First().ToString()).ConfigureAwait(false); if (string.IsNullOrEmpty(creationFactoryUrl)) { From 30cda2c768cf62cbf3ae0e30179beb1785110e30 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 14 Feb 2026 13:40:54 +0000 Subject: [PATCH 2/5] Refactor GetTypes/SetTypes to Types property in IExtendedResource - Introduced `Types` property in `IExtendedResource` and implemented it in `AbstractResource` and `AbstractResourceRecord`. - Marked `GetTypes` and `SetTypes` as `[Obsolete]` to encourage property usage. - Updated `AbstractResource.Types` setter to create a new `List` to ensure safe modification. - Maintained `List` return type in `AbstractResourceRecord.Types` via explicit interface implementation to avoid breaking changes. - Replaced all usages of `GetTypes()` and `SetTypes()` with `.Types` property across the codebase, including samples and client resources. - Eliminated `CS0618` warnings related to obsolete method calls. Co-authored-by: berezovskyi <64734+berezovskyi@users.noreply.github.com> --- .../Examples/OSLC4Net.Client.Samples/ETMSample.cs | 2 +- .../Examples/OSLC4Net.Client.Samples/EWMSample.cs | 4 ++-- OSLC4Net_SDK/OSLC4Net.ChangeManagement/ChangeRequest.cs | 4 ++-- .../Oslc/Resources/ArchitectureLinkType.cs | 4 ++-- .../Oslc/Resources/ArchitectureResource.cs | 4 ++-- .../OSLC4Net.Client/Oslc/Resources/AutomationPlan.cs | 4 ++-- .../OSLC4Net.Client/Oslc/Resources/AutomationRequest.cs | 4 ++-- .../OSLC4Net.Client/Oslc/Resources/AutomationResult.cs | 4 ++-- .../OSLC4Net.Client/Oslc/Resources/ChangeRequest.cs | 4 ++-- .../OSLC4Net.Client/Oslc/Resources/ParameterInstance.cs | 4 ++-- .../OSLC4Net.Client/Oslc/Resources/QmResource.cs | 4 ++-- .../OSLC4Net.Client/Oslc/Resources/RequirementBase.cs | 4 ++-- .../OSLC4Net.Core.DotNetRdfProvider/DotNetRdfHelper.cs | 4 ++-- OSLC4Net_SDK/OSLC4Net.Core/Model/AbstractResource.cs | 9 +++++++++ .../OSLC4Net.Core/Model/AbstractResourceRecord.cs | 9 +++++++++ OSLC4Net_SDK/OSLC4Net.Core/Model/IExtendedResource.cs | 9 +++++++++ 16 files changed, 52 insertions(+), 25 deletions(-) diff --git a/OSLC4Net_SDK/Examples/OSLC4Net.Client.Samples/ETMSample.cs b/OSLC4Net_SDK/Examples/OSLC4Net.Client.Samples/ETMSample.cs index ef76708b..62d35bfb 100644 --- a/OSLC4Net_SDK/Examples/OSLC4Net.Client.Samples/ETMSample.cs +++ b/OSLC4Net_SDK/Examples/OSLC4Net.Client.Samples/ETMSample.cs @@ -159,7 +159,7 @@ async Task RunAsync(string webContextUrl, string user, string passwd, string pro //Get the Creation Factory URL for test cases so that we can create a test case String testcaseCreation = await client.LookupCreationFactoryAsync( serviceProviderUrl, OSLCConstants.OSLC_QM_V2, - testcase.GetTypes().First().ToString()).ConfigureAwait(false); + testcase.Types.First().ToString()).ConfigureAwait(false); //Create the test case HttpResponseMessage creationResponse = await client.CreateResourceRawAsync( diff --git a/OSLC4Net_SDK/Examples/OSLC4Net.Client.Samples/EWMSample.cs b/OSLC4Net_SDK/Examples/OSLC4Net.Client.Samples/EWMSample.cs index 11d3b603..3a6790b2 100644 --- a/OSLC4Net_SDK/Examples/OSLC4Net.Client.Samples/EWMSample.cs +++ b/OSLC4Net_SDK/Examples/OSLC4Net.Client.Samples/EWMSample.cs @@ -195,7 +195,7 @@ public async Task RunScenarioAsync(string webContextUrl, string user, string pas //Get the Creation Factory URL for change requests so that we can create one String changeRequestCreation = await client.LookupCreationFactoryAsync( serviceProviderUrl, OSLCConstants.OSLC_CM_V2, - changeRequest.GetTypes().First().ToString()).ConfigureAwait(false); + changeRequest.Types.First().ToString()).ConfigureAwait(false); //Create the change request HttpResponseMessage creationResponse = await client.CreateResourceRawAsync( @@ -271,7 +271,7 @@ protected override void PrintResourceInfo(ChangeRequest cr) string creationFactoryUrl = await client.LookupCreationFactoryAsync( serviceProviderUrl, OSLCConstants.OSLC_CM_V2, - changeRequest.GetTypes().First().ToString()).ConfigureAwait(false); + changeRequest.Types.First().ToString()).ConfigureAwait(false); if (string.IsNullOrEmpty(creationFactoryUrl)) { diff --git a/OSLC4Net_SDK/OSLC4Net.ChangeManagement/ChangeRequest.cs b/OSLC4Net_SDK/OSLC4Net.ChangeManagement/ChangeRequest.cs index 6c200c6a..0354aa46 100644 --- a/OSLC4Net_SDK/OSLC4Net.ChangeManagement/ChangeRequest.cs +++ b/OSLC4Net_SDK/OSLC4Net.ChangeManagement/ChangeRequest.cs @@ -354,7 +354,7 @@ public Uri GetInstanceShape() [Obsolete] public Uri[] GetRdfTypes() { - return GetTypes().ToArray(); + return Types.ToArray(); } [OslcDescription("This relationship is loosely coupled and has no specific meaning.")] @@ -716,7 +716,7 @@ public void SetModified(DateTime? modified) [Obsolete] public void SetRdfTypes(Uri[] rdfTypes) { - SetTypes(rdfTypes); + Types = rdfTypes; } public void SetRelatedChangeRequests(Link[] relatedChangeRequests) diff --git a/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/ArchitectureLinkType.cs b/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/ArchitectureLinkType.cs index b53cebb3..5c969a46 100644 --- a/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/ArchitectureLinkType.cs +++ b/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/ArchitectureLinkType.cs @@ -147,7 +147,7 @@ public Uri GetInstanceShape() [Obsolete("User GetTypes() or .Types instead")] public Uri[] GetRdfTypes() { - return GetTypes().ToArray(); + return Types.ToArray(); } [OslcDescription("The scope of a resource is a Uri for the resource's OSLC Service Provider.")] @@ -212,7 +212,7 @@ public void SetModified(DateTime? modified) [Obsolete("User SetTypes() or .Types instead")] public void SetRdfTypes(Uri[] rdfTypes) { - SetTypes(rdfTypes); + Types = rdfTypes; } public void SetServiceProvider(Uri serviceProvider) diff --git a/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/ArchitectureResource.cs b/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/ArchitectureResource.cs index 850d2b4a..2623751b 100644 --- a/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/ArchitectureResource.cs +++ b/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/ArchitectureResource.cs @@ -147,7 +147,7 @@ public Uri GetInstanceShape() [Obsolete("User GetTypes() or .Types instead")] public Uri[] GetRdfTypes() { - return GetTypes().ToArray(); + return Types.ToArray(); } [OslcDescription("A short string representation for the type, example 'Defect'.")] @@ -236,7 +236,7 @@ public void SetModified(DateTime? modified) [Obsolete("User SetTypes() or .Types instead")] public void SetRdfTypes(Uri[] rdfTypes) { - SetTypes(rdfTypes); + Types = rdfTypes; } public void SetDctermsTypes(string[] dctermsTypes) diff --git a/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/AutomationPlan.cs b/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/AutomationPlan.cs index 5d346ff4..d6e9094f 100644 --- a/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/AutomationPlan.cs +++ b/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/AutomationPlan.cs @@ -153,7 +153,7 @@ public Uri GetInstanceShape() [Obsolete("User GetTypes() or .Types instead")] public Uri[] GetRdfTypes() { - return GetTypes().ToArray(); + return Types.ToArray(); } [OslcDescription("The scope of a resource is a Uri for the resource's OSLC Service Provider.")] @@ -245,7 +245,7 @@ public void SetModified(DateTime? modified) [Obsolete("User SetTypes() or .Types instead")] public void SetRdfTypes(Uri[] rdfTypes) { - SetTypes(rdfTypes); + Types = rdfTypes; } public void SetServiceProvider(Uri serviceProvider) diff --git a/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/AutomationRequest.cs b/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/AutomationRequest.cs index 903b0e0a..029ebbd6 100644 --- a/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/AutomationRequest.cs +++ b/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/AutomationRequest.cs @@ -163,7 +163,7 @@ public Uri GetInstanceShape() [Obsolete("User GetTypes() or .Types instead")] public Uri[] GetRdfTypes() { - return GetTypes().ToArray(); + return Types.ToArray(); } [OslcDescription("The scope of a resource is a Uri for the resource's OSLC Service Provider.")] @@ -306,7 +306,7 @@ public void SetModified(DateTime? modified) [Obsolete("User SetTypes() or .Types instead")] public void SetRdfTypes(Uri[] rdfTypes) { - SetTypes(rdfTypes); + Types = rdfTypes; } public void SetServiceProvider(Uri serviceProvider) diff --git a/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/AutomationResult.cs b/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/AutomationResult.cs index 2f967150..06109d50 100644 --- a/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/AutomationResult.cs +++ b/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/AutomationResult.cs @@ -173,7 +173,7 @@ public Uri GetInstanceShape() [Obsolete("User GetTypes() or .Types instead")] public Uri[] GetRdfTypes() { - return GetTypes().ToArray(); + return Types.ToArray(); } [OslcDescription("The scope of a resource is a Uri for the resource's OSLC Service Provider.")] @@ -363,7 +363,7 @@ public void setModified(DateTime? modified) [Obsolete("User SetTypes() or .Types instead")] public void SetRdfTypes(Uri[] rdfTypes) { - SetTypes(rdfTypes); + Types = rdfTypes; } public void setServiceProvider(Uri serviceProvider) diff --git a/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/ChangeRequest.cs b/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/ChangeRequest.cs index 73576ae4..2c8d366d 100644 --- a/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/ChangeRequest.cs +++ b/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/ChangeRequest.cs @@ -341,7 +341,7 @@ public Uri GetInstanceShape() [Obsolete("User GetTypes() or .Types instead")] public Uri[] GetRdfTypes() { - return GetTypes().ToArray(); + return Types.ToArray(); } [OslcDescription("This relationship is loosely coupled and has no specific meaning.")] @@ -696,7 +696,7 @@ public void SetModified(DateTime? modified) [Obsolete("User SetTypes() or .Types instead")] public void SetRdfTypes(Uri[] rdfTypes) { - SetTypes(rdfTypes); + Types = rdfTypes; } public void SetRelatedChangeRequests(Link[] relatedChangeRequests) diff --git a/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/ParameterInstance.cs b/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/ParameterInstance.cs index 29c0a208..caeb6fef 100644 --- a/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/ParameterInstance.cs +++ b/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/ParameterInstance.cs @@ -95,7 +95,7 @@ public Uri GetInstanceShape() [Obsolete("User GetTypes() or .Types instead")] public Uri[] GetRdfTypes() { - return GetTypes().ToArray(); + return Types.ToArray(); } [OslcDescription("The scope of a resource is a Uri for the resource's OSLC Service Provider.")] @@ -130,7 +130,7 @@ public void SetInstanceShape(Uri instanceShape) [Obsolete("User SetTypes() or .Types instead")] public void SetRdfTypes(Uri[] rdfTypes) { - SetTypes(rdfTypes); + Types = rdfTypes; } public void SetServiceProvider(Uri serviceProvider) diff --git a/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/QmResource.cs b/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/QmResource.cs index e8f596a1..48904161 100644 --- a/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/QmResource.cs +++ b/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/QmResource.cs @@ -90,7 +90,7 @@ public Uri GetInstanceShape() [Obsolete("User GetTypes() or .Types instead")] public Uri[] GetRdfTypes() { - return GetTypes().ToArray(); + return Types.ToArray(); } [OslcDescription("The scope of a resource is a Uri for the resource's OSLC Service Provider.")] @@ -136,7 +136,7 @@ public void SetModified(DateTime? modified) [Obsolete("User SetTypes() or .Types instead")] public void SetRdfTypes(Uri[] rdfTypes) { - SetTypes(rdfTypes); + Types = rdfTypes; } public void SetServiceProvider(Uri serviceProvider) diff --git a/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/RequirementBase.cs b/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/RequirementBase.cs index 4720aa07..5747b813 100644 --- a/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/RequirementBase.cs +++ b/OSLC4Net_SDK/OSLC4Net.Client/Oslc/Resources/RequirementBase.cs @@ -390,7 +390,7 @@ public Uri[] GetCreators() [Obsolete("User GetTypes() or .Types instead")] public Uri[] GetRdfTypes() { - return GetTypes().ToArray(); + return Types.ToArray(); } [OslcDescription("The scope of a resource is a Uri for the resource's OSLC Service Provider.")] @@ -611,7 +611,7 @@ public void SetModified(DateTime? modified) [Obsolete("User SetTypes() or .Types instead")] public void SetRdfTypes(Uri[] rdfTypes) { - SetTypes(rdfTypes); + Types = rdfTypes; } public void SetServiceProvider(Uri? serviceProvider) diff --git a/OSLC4Net_SDK/OSLC4Net.Core.DotNetRdfProvider/DotNetRdfHelper.cs b/OSLC4Net_SDK/OSLC4Net.Core.DotNetRdfProvider/DotNetRdfHelper.cs index a140c81f..7cf4e336 100644 --- a/OSLC4Net_SDK/OSLC4Net.Core.DotNetRdfProvider/DotNetRdfHelper.cs +++ b/OSLC4Net_SDK/OSLC4Net.Core.DotNetRdfProvider/DotNetRdfHelper.cs @@ -1322,7 +1322,7 @@ private static void HandleExtendedProperties(Type resourceType, IExtendedResource extendedResource, IDictionary? properties) { - foreach (var type in extendedResource.GetTypes()) + foreach (var type in extendedResource.Types) { var propertyName = type.ToString(); @@ -1433,7 +1433,7 @@ private static void HandleExtendedValue(Type objType, nestedResource = graph.CreateBlankNode(); } - foreach (var type in any.GetTypes()) + foreach (var type in any.Types) { var propertyName = type.ToString(); diff --git a/OSLC4Net_SDK/OSLC4Net.Core/Model/AbstractResource.cs b/OSLC4Net_SDK/OSLC4Net.Core/Model/AbstractResource.cs index 072216d8..0ed91219 100644 --- a/OSLC4Net_SDK/OSLC4Net.Core/Model/AbstractResource.cs +++ b/OSLC4Net_SDK/OSLC4Net.Core/Model/AbstractResource.cs @@ -79,6 +79,7 @@ public IDictionary GetExtendedProperties() [OslcName("type")] [OslcPropertyDefinition(OslcConstants.RDF_NAMESPACE + "type")] [OslcTitle("Types")] + [Obsolete("Use .Types property instead")] public ICollection GetTypes() { return types; @@ -88,11 +89,19 @@ public ICollection GetTypes() /// Set the RDF types /// /// + [Obsolete("Use .Types property instead")] public void SetTypes(ICollection types) { this.types = types; } + /// + public ICollection Types + { + get => types; + set => types = new List(value); + } + /// /// Add an additional RDF type /// diff --git a/OSLC4Net_SDK/OSLC4Net.Core/Model/AbstractResourceRecord.cs b/OSLC4Net_SDK/OSLC4Net.Core/Model/AbstractResourceRecord.cs index 1eec83dc..3991eb84 100644 --- a/OSLC4Net_SDK/OSLC4Net.Core/Model/AbstractResourceRecord.cs +++ b/OSLC4Net_SDK/OSLC4Net.Core/Model/AbstractResourceRecord.cs @@ -18,6 +18,13 @@ public abstract record AbstractResourceRecord : IExtendedResource public List Types { get; private set; } = new(); + /// + ICollection IExtendedResource.Types + { + get => Types; + set => Types = new List(value); + } + public IDictionary ExtendedProperties { get; private set; } = new Dictionary(); @@ -47,12 +54,14 @@ public void SetAbout(Uri about) [OslcName("type")] [OslcPropertyDefinition(OslcConstants.RDF_NAMESPACE + "type")] [OslcTitle("Types")] + [Obsolete("Use .Types property instead")] public ICollection GetTypes() { return Types; } /// + [Obsolete("Use .Types property instead")] public void SetTypes(ICollection types) { Types = new List(types); diff --git a/OSLC4Net_SDK/OSLC4Net.Core/Model/IExtendedResource.cs b/OSLC4Net_SDK/OSLC4Net.Core/Model/IExtendedResource.cs index 7cebcf18..e4995437 100644 --- a/OSLC4Net_SDK/OSLC4Net.Core/Model/IExtendedResource.cs +++ b/OSLC4Net_SDK/OSLC4Net.Core/Model/IExtendedResource.cs @@ -42,6 +42,7 @@ public interface IExtendedResource : IResource [OslcName("type")] [OslcPropertyDefinition(OslcConstants.RDF_NAMESPACE + "type")] [OslcTitle("Types")] + [Obsolete("Use .Types property instead")] ICollection GetTypes(); /// @@ -50,8 +51,16 @@ public interface IExtendedResource : IResource /// OslcResourceShape#describes() annotation. /// /// + [Obsolete("Use .Types property instead")] void SetTypes(ICollection types); + /// + /// Gets or sets the RDF types of this resource. These types will be added to the + /// serialization of the resource in addition to the + /// OslcResourceShape#describes() annotation. + /// + ICollection Types { get; set; } + /// /// Adds an RDF type to this resource. These types will be added to the /// serialization of the resource in addition to the From 5ef0156b90b0af6152d10e86f28fbc087234e271 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 14 Feb 2026 15:06:22 +0000 Subject: [PATCH 3/5] Refactor GetTypes to Types property and move OSLC attributes - Replaced `GetTypes()` and `SetTypes()` with `Types` property in `IExtendedResource`, `AbstractResource`, and `AbstractResourceRecord`. - Moved OSLC metadata attributes (`[OslcDescription]`, `[OslcName]`, `[OslcPropertyDefinition]`, `[OslcTitle]`) from `GetTypes()` method to the new `Types` property. - Updated `ResourceShapeFactory` to support inspecting both properties and methods for OSLC metadata, ensuring correct resource shape generation. - Marked legacy `GetTypes()` and `SetTypes()` methods as `[Obsolete]`. - Updated usage in `DotNetRdfHelper`, `ETMSample`, `EWMSample`, and generated resources to use `.Types`. - Ensured `AbstractResource.Types` setter performs a safe list copy. - Maintained `AbstractResourceRecord.Types` as `List` via explicit interface implementation. Co-authored-by: berezovskyi <64734+berezovskyi@users.noreply.github.com> --- .../OSLC4Net.Core/Model/AbstractResource.cs | 8 +-- .../Model/AbstractResourceRecord.cs | 10 +-- .../OSLC4Net.Core/Model/IExtendedResource.cs | 8 +-- .../Model/ResourceShapeFactory.cs | 63 ++++++++++++++++--- 4 files changed, 69 insertions(+), 20 deletions(-) diff --git a/OSLC4Net_SDK/OSLC4Net.Core/Model/AbstractResource.cs b/OSLC4Net_SDK/OSLC4Net.Core/Model/AbstractResource.cs index 0ed91219..51682f06 100644 --- a/OSLC4Net_SDK/OSLC4Net.Core/Model/AbstractResource.cs +++ b/OSLC4Net_SDK/OSLC4Net.Core/Model/AbstractResource.cs @@ -75,10 +75,6 @@ public IDictionary GetExtendedProperties() /// Get the RDF types /// /// - [OslcDescription("The resource type URIs.")] - [OslcName("type")] - [OslcPropertyDefinition(OslcConstants.RDF_NAMESPACE + "type")] - [OslcTitle("Types")] [Obsolete("Use .Types property instead")] public ICollection GetTypes() { @@ -96,6 +92,10 @@ public void SetTypes(ICollection types) } /// + [OslcDescription("The resource type URIs.")] + [OslcName("type")] + [OslcPropertyDefinition(OslcConstants.RDF_NAMESPACE + "type")] + [OslcTitle("Types")] public ICollection Types { get => types; diff --git a/OSLC4Net_SDK/OSLC4Net.Core/Model/AbstractResourceRecord.cs b/OSLC4Net_SDK/OSLC4Net.Core/Model/AbstractResourceRecord.cs index 3991eb84..775ab223 100644 --- a/OSLC4Net_SDK/OSLC4Net.Core/Model/AbstractResourceRecord.cs +++ b/OSLC4Net_SDK/OSLC4Net.Core/Model/AbstractResourceRecord.cs @@ -16,9 +16,13 @@ public abstract record AbstractResourceRecord : IExtendedResource { public Uri About { get; set; } + /// + [OslcDescription("The resource type URIs.")] + [OslcName("type")] + [OslcPropertyDefinition(OslcConstants.RDF_NAMESPACE + "type")] + [OslcTitle("Types")] public List Types { get; private set; } = new(); - /// ICollection IExtendedResource.Types { get => Types; @@ -50,10 +54,6 @@ public void SetAbout(Uri about) } /// - [OslcDescription("The resource type URIs.")] - [OslcName("type")] - [OslcPropertyDefinition(OslcConstants.RDF_NAMESPACE + "type")] - [OslcTitle("Types")] [Obsolete("Use .Types property instead")] public ICollection GetTypes() { diff --git a/OSLC4Net_SDK/OSLC4Net.Core/Model/IExtendedResource.cs b/OSLC4Net_SDK/OSLC4Net.Core/Model/IExtendedResource.cs index e4995437..87185116 100644 --- a/OSLC4Net_SDK/OSLC4Net.Core/Model/IExtendedResource.cs +++ b/OSLC4Net_SDK/OSLC4Net.Core/Model/IExtendedResource.cs @@ -38,10 +38,6 @@ public interface IExtendedResource : IResource /// OslcResourceShape#describes() annotation /// /// - [OslcDescription("The resource type URIs.")] - [OslcName("type")] - [OslcPropertyDefinition(OslcConstants.RDF_NAMESPACE + "type")] - [OslcTitle("Types")] [Obsolete("Use .Types property instead")] ICollection GetTypes(); @@ -59,6 +55,10 @@ public interface IExtendedResource : IResource /// serialization of the resource in addition to the /// OslcResourceShape#describes() annotation. /// + [OslcDescription("The resource type URIs.")] + [OslcName("type")] + [OslcPropertyDefinition(OslcConstants.RDF_NAMESPACE + "type")] + [OslcTitle("Types")] ICollection Types { get; set; } /// diff --git a/OSLC4Net_SDK/OSLC4Net.Core/Model/ResourceShapeFactory.cs b/OSLC4Net_SDK/OSLC4Net.Core/Model/ResourceShapeFactory.cs index c0f2ccd4..e16b9ddc 100644 --- a/OSLC4Net_SDK/OSLC4Net.Core/Model/ResourceShapeFactory.cs +++ b/OSLC4Net_SDK/OSLC4Net.Core/Model/ResourceShapeFactory.cs @@ -146,10 +146,31 @@ private static ResourceShape CreateResourceShape(string baseURI, } } + foreach (var prop in resourceType.GetProperties()) + { + var propertyDefinitionAttribute = + InheritedMethodAttributeHelper.GetAttribute(prop); + if (propertyDefinitionAttribute != null) + { + var propertyDefinition = propertyDefinitionAttribute.value; + if (propertyDefinitions.Contains(propertyDefinition)) + { + throw new OslcCoreDuplicatePropertyDefinitionException(resourceType, + propertyDefinitionAttribute); + } + + propertyDefinitions.Add(propertyDefinition); + + var property = CreateProperty(baseURI, resourceType, prop, + propertyDefinitionAttribute, verifiedTypes); + resourceShape.AddProperty(property); + } + } + return resourceShape; } - private static Property CreateProperty(string baseURI, Type resourceType, MethodInfo method, + private static Property CreateProperty(string baseURI, Type resourceType, MemberInfo method, OslcPropertyDefinition propertyDefinitionAttribute, ISet verifiedTypes) { string name; @@ -160,7 +181,12 @@ private static Property CreateProperty(string baseURI, Type resourceType, Method } else { - name = GetDefaultPropertyName(method); + name = method switch + { + MethodInfo methodInfo => GetDefaultPropertyName(methodInfo), + PropertyInfo propertyInfo => GetDefaultPropertyName(propertyInfo), + _ => throw new ArgumentException("Unsupported member type", nameof(method)) + }; } var propertyDefinition = propertyDefinitionAttribute.value; @@ -171,7 +197,13 @@ private static Property CreateProperty(string baseURI, Type resourceType, Method propertyDefinitionAttribute); } - var returnType = method.ReturnType; + var returnType = method switch + { + MethodInfo methodInfo => methodInfo.ReturnType, + PropertyInfo propertyInfo => propertyInfo.PropertyType, + _ => throw new ArgumentException("Unsupported member type", nameof(method)) + }; + Occurs occurs; var occursAttribute = InheritedMethodAttributeHelper.GetAttribute(method); if (occursAttribute != null) @@ -330,6 +362,18 @@ private static Property CreateProperty(string baseURI, Type resourceType, Method return property; } + private static string GetDefaultPropertyName(PropertyInfo property) + { + var name = property.Name; + var lowercasedFirstCharacter = name.Substring(0, 1).ToLower(CultureInfo.InvariantCulture); + if (name.Length == 1) + { + return lowercasedFirstCharacter; + } + + return string.Concat(lowercasedFirstCharacter, name.AsSpan(1)); + } + private static string GetDefaultPropertyName(MethodInfo method) { var methodName = method.Name; @@ -423,10 +467,15 @@ private static void ValidateSetMethodExists(Type resourceType, MethodInfo getMet } } - private static void ValidateUserSpecifiedOccurs(Type resourceType, MethodInfo method, + private static void ValidateUserSpecifiedOccurs(Type resourceType, MemberInfo method, OslcOccurs occursAttribute) { - var returnType = method.ReturnType; + var returnType = method switch + { + MethodInfo methodInfo => methodInfo.ReturnType, + PropertyInfo propertyInfo => propertyInfo.PropertyType, + _ => throw new ArgumentException("Unsupported member type", nameof(method)) + }; var occurs = occursAttribute.value; if (returnType.IsArray || @@ -449,7 +498,7 @@ private static void ValidateUserSpecifiedOccurs(Type resourceType, MethodInfo me } } - private static void ValidateUserSpecifiedValueType(Type resourceType, MethodInfo method, + private static void ValidateUserSpecifiedValueType(Type resourceType, MemberInfo method, ValueType userSpecifiedValueType, Type componentType) { var calculatedValueType = TYPE_TO_VALUE_TYPE[componentType]; @@ -488,7 +537,7 @@ private static void ValidateUserSpecifiedValueType(Type resourceType, MethodInfo throw new OslcCoreInvalidValueTypeException(resourceType, method, userSpecifiedValueType); } - private static void ValidateUserSpecifiedRepresentation(Type resourceType, MethodInfo method, + private static void ValidateUserSpecifiedRepresentation(Type resourceType, MemberInfo method, Representation userSpecifiedRepresentation, Type componentType) { // If user-specified representation is reference and component is not Uri From 5b1b1de7666eddccdbdfcb86c412d75f0a361665 Mon Sep 17 00:00:00 2001 From: Andrew Berezovskyi Date: Sat, 14 Feb 2026 23:05:07 +0100 Subject: [PATCH 4/5] fix: more property support --- .../OslcCoreInvalidOccursException.cs | 58 +++++++-------- .../OslcCoreInvalidPropertyTypeException.cs | 4 +- .../OslcCoreInvalidRepresentationException.cs | 56 +++++++------- .../OslcCoreInvalidValueTypeException.cs | 4 +- OSLC4Net_SDK/OSLC4Net.Core/Model/Property.cs | 9 ++- .../Model/ResourceShapeFactory.cs | 11 ++- .../ResourceShapePropertyAnnotationTests.cs | 74 +++++++++++++++++++ .../TypesPropertyRoundtripTests.cs | 69 +++++++++++++++++ .../ResourceShapeFactoryTests.cs | 31 ++++---- 9 files changed, 238 insertions(+), 78 deletions(-) create mode 100644 OSLC4Net_SDK/Tests/OSLC4Net.Core.DotNetRdfProviderTests/ResourceShapePropertyAnnotationTests.cs create mode 100644 OSLC4Net_SDK/Tests/OSLC4Net.Core.DotNetRdfProviderTests/TypesPropertyRoundtripTests.cs diff --git a/OSLC4Net_SDK/OSLC4Net.Core/Exceptions/OslcCoreInvalidOccursException.cs b/OSLC4Net_SDK/OSLC4Net.Core/Exceptions/OslcCoreInvalidOccursException.cs index 1e2ef904..192d4ffc 100644 --- a/OSLC4Net_SDK/OSLC4Net.Core/Exceptions/OslcCoreInvalidOccursException.cs +++ b/OSLC4Net_SDK/OSLC4Net.Core/Exceptions/OslcCoreInvalidOccursException.cs @@ -1,29 +1,29 @@ -/******************************************************************************* - * Copyright (c) 2012 IBM Corporation. - * Copyright (c) 2025 Andrii Berezovskyi and OSLC4Net contributors. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v. 1.0 which accompanies this distribution. - * - * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - *******************************************************************************/ - -using System.Reflection; -using OSLC4Net.Core.Attribute; -using OSLC4Net.Core.Model; - -namespace OSLC4Net.Core.Exceptions; - -public class OslcCoreInvalidOccursException( - Type resourceType, - MethodInfo method, - OslcOccurs oslcOccurs) : OslcCoreApplicationException( - $"OSLC1003: Invalid occurs annotation {OccursExtension.ToString(oslcOccurs.value)} for method {method.Name} of class {resourceType.Name}") -{ - public Type ResourceType { get; } = resourceType; - public MethodInfo Method { get; } = method; - public OslcOccurs OslcOccurs { get; } = oslcOccurs; -} +/******************************************************************************* + * Copyright (c) 2012 IBM Corporation. + * Copyright (c) 2025 Andrii Berezovskyi and OSLC4Net contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + *******************************************************************************/ + +using System.Reflection; +using OSLC4Net.Core.Attribute; +using OSLC4Net.Core.Model; + +namespace OSLC4Net.Core.Exceptions; + +public class OslcCoreInvalidOccursException( + Type resourceType, + MemberInfo method, + OslcOccurs oslcOccurs) : OslcCoreApplicationException( + $"OSLC1003: Invalid occurs annotation {OccursExtension.ToString(oslcOccurs.value)} for method {method.Name} of class {resourceType.Name}") +{ + public Type ResourceType { get; } = resourceType; + public MemberInfo Method { get; } = method; + public OslcOccurs OslcOccurs { get; } = oslcOccurs; +} diff --git a/OSLC4Net_SDK/OSLC4Net.Core/Exceptions/OslcCoreInvalidPropertyTypeException.cs b/OSLC4Net_SDK/OSLC4Net.Core/Exceptions/OslcCoreInvalidPropertyTypeException.cs index 10456609..bb60c978 100644 --- a/OSLC4Net_SDK/OSLC4Net.Core/Exceptions/OslcCoreInvalidPropertyTypeException.cs +++ b/OSLC4Net_SDK/OSLC4Net.Core/Exceptions/OslcCoreInvalidPropertyTypeException.cs @@ -20,11 +20,11 @@ namespace OSLC4Net.Core.Exceptions; /// public class OslcCoreInvalidPropertyTypeException( Type resourceType, - MethodInfo method, + MemberInfo method, Type returnType) : OslcCoreApplicationException( $"OSLC1005: Invalid property type {returnType.Name} returned by method {method.Name} of class {resourceType.Name}") { public Type ResourceType { get; } = resourceType; - public MethodInfo Method { get; } = method; + public MemberInfo Method { get; } = method; public Type ReturnType { get; } = returnType; } diff --git a/OSLC4Net_SDK/OSLC4Net.Core/Exceptions/OslcCoreInvalidRepresentationException.cs b/OSLC4Net_SDK/OSLC4Net.Core/Exceptions/OslcCoreInvalidRepresentationException.cs index 80121b8c..18140a6b 100644 --- a/OSLC4Net_SDK/OSLC4Net.Core/Exceptions/OslcCoreInvalidRepresentationException.cs +++ b/OSLC4Net_SDK/OSLC4Net.Core/Exceptions/OslcCoreInvalidRepresentationException.cs @@ -1,28 +1,28 @@ -/******************************************************************************* - * Copyright (c) 2012 IBM Corporation. - * Copyright (c) 2025 Andrii Berezovskyi and OSLC4Net contributors. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v. 1.0 which accompanies this distribution. - * - * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - *******************************************************************************/ - -using System.Reflection; -using OSLC4Net.Core.Model; - -namespace OSLC4Net.Core.Exceptions; - -public class OslcCoreInvalidRepresentationException( - Type resourceType, - MethodInfo method, - Representation representation) : OslcCoreApplicationException( - $"OSLC1006: Invalid representation {RepresentationExtension.ToString(representation)} defined for method {method.Name} of class {resourceType.Name}") -{ - public Type ResourceType { get; } = resourceType; - public MethodInfo Method { get; } = method; - public Representation Representation { get; } = representation; -} +/******************************************************************************* + * Copyright (c) 2012 IBM Corporation. + * Copyright (c) 2025 Andrii Berezovskyi and OSLC4Net contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + *******************************************************************************/ + +using System.Reflection; +using OSLC4Net.Core.Model; + +namespace OSLC4Net.Core.Exceptions; + +public class OslcCoreInvalidRepresentationException( + Type resourceType, + MemberInfo method, + Representation representation) : OslcCoreApplicationException( + $"OSLC1006: Invalid representation {RepresentationExtension.ToString(representation)} defined for method {method.Name} of class {resourceType.Name}") +{ + public Type ResourceType { get; } = resourceType; + public MemberInfo Method { get; } = method; + public Representation Representation { get; } = representation; +} diff --git a/OSLC4Net_SDK/OSLC4Net.Core/Exceptions/OslcCoreInvalidValueTypeException.cs b/OSLC4Net_SDK/OSLC4Net.Core/Exceptions/OslcCoreInvalidValueTypeException.cs index 38ba72b6..e0424b61 100644 --- a/OSLC4Net_SDK/OSLC4Net.Core/Exceptions/OslcCoreInvalidValueTypeException.cs +++ b/OSLC4Net_SDK/OSLC4Net.Core/Exceptions/OslcCoreInvalidValueTypeException.cs @@ -19,11 +19,11 @@ namespace OSLC4Net.Core.Exceptions; public class OslcCoreInvalidValueTypeException( Type resourceType, - MethodInfo method, + MemberInfo method, ValueType valueType) : OslcCoreApplicationException( $"OSLC1007: Invalid value type {ValueTypeExtension.ToString(valueType)} defined for method {method.Name} of class {resourceType.Name}") { public Type ResourceType { get; } = resourceType; - public MethodInfo Method { get; } = method; + public MemberInfo Method { get; } = method; public ValueType ValueType { get; } = valueType; } diff --git a/OSLC4Net_SDK/OSLC4Net.Core/Model/Property.cs b/OSLC4Net_SDK/OSLC4Net.Core/Model/Property.cs index f9b19ccd..d349b3f2 100644 --- a/OSLC4Net_SDK/OSLC4Net.Core/Model/Property.cs +++ b/OSLC4Net_SDK/OSLC4Net.Core/Model/Property.cs @@ -61,7 +61,14 @@ public Property(string name, public int CompareTo(Property o) { - return name.CompareTo(o.GetName()); + var nameComparison = string.Compare(name, o.GetName(), StringComparison.Ordinal); + if (nameComparison != 0) + { + return nameComparison; + } + + return Uri.Compare(propertyDefinition, o.propertyDefinition, + UriComponents.AbsoluteUri, UriFormat.UriEscaped, StringComparison.Ordinal); } public void AddAllowedValue(string allowedValue) diff --git a/OSLC4Net_SDK/OSLC4Net.Core/Model/ResourceShapeFactory.cs b/OSLC4Net_SDK/OSLC4Net.Core/Model/ResourceShapeFactory.cs index e16b9ddc..9361bf86 100644 --- a/OSLC4Net_SDK/OSLC4Net.Core/Model/ResourceShapeFactory.cs +++ b/OSLC4Net_SDK/OSLC4Net.Core/Model/ResourceShapeFactory.cs @@ -53,6 +53,7 @@ static ResourceShapeFactory() // Object types TYPE_TO_VALUE_TYPE[typeof(BigInteger)] = ValueType.Integer; TYPE_TO_VALUE_TYPE[typeof(DateTime)] = ValueType.DateTime; + TYPE_TO_VALUE_TYPE[typeof(DateTimeOffset)] = ValueType.DateTime; TYPE_TO_VALUE_TYPE[typeof(Uri)] = ValueType.Resource; TYPE_TO_VALUE_TYPE[typeof(ICollection)] = ValueType.Resource; TYPE_TO_VALUE_TYPE[typeof(IEnumerable)] = ValueType.Resource; @@ -391,7 +392,7 @@ private static string GetDefaultPropertyName(MethodInfo method) return string.Concat(lowercasedFirstCharacter, methodName.AsSpan(startingIndex + 1)); } - private static ValueType GetDefaultValueType(Type resourceType, MethodInfo method, + private static ValueType GetDefaultValueType(Type resourceType, MemberInfo method, Type componentType) { var valueType = TYPE_TO_VALUE_TYPE[componentType]; @@ -425,7 +426,7 @@ private static Occurs GetDefaultOccurs(Type type) return Occurs.ZeroOrOne; } - private static Type GetComponentType(Type resourceType, MethodInfo method, Type type) + private static Type GetComponentType(Type resourceType, MemberInfo method, Type type) { if (type.IsArray) { @@ -444,6 +445,12 @@ private static Type GetComponentType(Type resourceType, MethodInfo method, Type throw new OslcCoreInvalidPropertyTypeException(resourceType, method, type); } + var underlyingType = Nullable.GetUnderlyingType(type); + if (underlyingType != null) + { + return underlyingType; + } + return type; } diff --git a/OSLC4Net_SDK/Tests/OSLC4Net.Core.DotNetRdfProviderTests/ResourceShapePropertyAnnotationTests.cs b/OSLC4Net_SDK/Tests/OSLC4Net.Core.DotNetRdfProviderTests/ResourceShapePropertyAnnotationTests.cs new file mode 100644 index 00000000..3329158f --- /dev/null +++ b/OSLC4Net_SDK/Tests/OSLC4Net.Core.DotNetRdfProviderTests/ResourceShapePropertyAnnotationTests.cs @@ -0,0 +1,74 @@ +using OSLC4Net.ChangeManagement; +using OSLC4Net.Core.Attribute; +using OSLC4Net.Core.Model; + +namespace OSLC4Net.Core.DotNetRdfProviderTests; + +[OslcResourceShape(title = "Test Shape", describes = new[] { "http://example.com/TestResource" })] +[OslcNamespace("http://example.com/ns#")] +public class TestResourceWithTypesProperty : AbstractResource +{ + [OslcDescription("Test property")] + [OslcName("name")] + [OslcPropertyDefinition("http://example.com/ns#name")] + [OslcTitle("Name")] + public string Name { get; set; } = ""; +} + +public class ResourceShapePropertyAnnotationTests +{ + [Test] + public async Task CreateResourceShape_DiscoversTypesPropertyAnnotation() + { + var shape = ResourceShapeFactory.CreateResourceShape( + "http://example.com", + OslcConstants.PATH_RESOURCE_SHAPES, + "changeRequest", + typeof(ChangeRequest)); + + var properties = shape.GetProperties(); + var typeProperty = properties.FirstOrDefault(p => + p.GetPropertyDefinition().ToString() == OslcConstants.RDF_NAMESPACE + "type"); + + await Assert.That(typeProperty).IsNotNull(); + await Assert.That(typeProperty!.GetName()).IsEqualTo("type"); + await Assert.That(typeProperty.GetTitle()).IsEqualTo("Types"); + } + + [Test] + public async Task CreateResourceShape_NoDuplicateRdfTypeProperty() + { + var shape = ResourceShapeFactory.CreateResourceShape( + "http://example.com", + OslcConstants.PATH_RESOURCE_SHAPES, + "changeRequest", + typeof(ChangeRequest)); + + var properties = shape.GetProperties(); + var typeProperties = properties.Where(p => + p.GetPropertyDefinition().ToString() == OslcConstants.RDF_NAMESPACE + "type").ToList(); + + // Should be exactly one rdf:type property (from the Types property, not from GetTypes) + await Assert.That(typeProperties.Count).IsEqualTo(1); + } + + [Test] + public async Task CreateResourceShape_MinimalResource_DiscoversTypesAndCustomProperty() + { + var shape = ResourceShapeFactory.CreateResourceShape( + "http://example.com", + OslcConstants.PATH_RESOURCE_SHAPES, + "testResource", + typeof(TestResourceWithTypesProperty)); + + var properties = shape.GetProperties(); + + var nameProperty = properties.FirstOrDefault(p => + p.GetPropertyDefinition().ToString() == "http://example.com/ns#name"); + await Assert.That(nameProperty).IsNotNull(); + + var typeProperty = properties.FirstOrDefault(p => + p.GetPropertyDefinition().ToString() == OslcConstants.RDF_NAMESPACE + "type"); + await Assert.That(typeProperty).IsNotNull(); + } +} diff --git a/OSLC4Net_SDK/Tests/OSLC4Net.Core.DotNetRdfProviderTests/TypesPropertyRoundtripTests.cs b/OSLC4Net_SDK/Tests/OSLC4Net.Core.DotNetRdfProviderTests/TypesPropertyRoundtripTests.cs new file mode 100644 index 00000000..e2a4c0ca --- /dev/null +++ b/OSLC4Net_SDK/Tests/OSLC4Net.Core.DotNetRdfProviderTests/TypesPropertyRoundtripTests.cs @@ -0,0 +1,69 @@ +using System.Net.Http.Headers; +using OSLC4Net.ChangeManagement; +using OSLC4Net.Core.DotNetRdfProvider; +using OSLC4Net.Core.Model; + +namespace OSLC4Net.Core.DotNetRdfProviderTests; + +public class TypesPropertyRoundtripTests +{ + [Test] + public async Task Types_RoundtripsViaSerialization() + { + var cr = new ChangeRequest(new Uri("http://example.com/cr/1")); + cr.Types = new List + { + new Uri(Constants.CHANGE_MANAGEMENT_NAMESPACE + "ChangeRequest"), + new Uri(Constants.CHANGE_MANAGEMENT_NAMESPACE + "Defect") + }; + + var formatter = new RdfXmlMediaTypeFormatter(); + var rdfXml = await RdfHelpers.SerializeAsync(formatter, cr, + OslcMediaType.APPLICATION_RDF_XML_TYPE); + + var deserialized = await RdfHelpers.DeserializeAsync(formatter, rdfXml, + OslcMediaType.APPLICATION_RDF_XML_TYPE); + + await Assert.That(deserialized).IsNotNull(); + await Assert.That(deserialized!.Types.Count).IsEqualTo(cr.Types.Count); + + foreach (var type in cr.Types) + { + await Assert.That(deserialized.Types).Contains(type); + } + } + + [Test] + public async Task Types_PropertyAndGetTypesShareBackingField() + { + var cr = new ChangeRequest(new Uri("http://example.com/cr/2")); + var typeUri = new Uri(Constants.CHANGE_MANAGEMENT_NAMESPACE + "ChangeRequest"); + + cr.Types = new List { typeUri }; + +#pragma warning disable CS0618 + var fromMethod = cr.GetTypes(); +#pragma warning restore CS0618 + + await Assert.That(fromMethod).Contains(typeUri); + await Assert.That(fromMethod.Count).IsEqualTo(1); + } + + [Test] + public async Task Types_EmptyCollectionRoundtrips() + { + var cr = new ChangeRequest(new Uri("http://example.com/cr/3")); + // Don't set any types - should roundtrip as empty + + var formatter = new RdfXmlMediaTypeFormatter(); + var rdfXml = await RdfHelpers.SerializeAsync(formatter, cr, + OslcMediaType.APPLICATION_RDF_XML_TYPE); + + var deserialized = await RdfHelpers.DeserializeAsync(formatter, rdfXml, + OslcMediaType.APPLICATION_RDF_XML_TYPE); + + await Assert.That(deserialized).IsNotNull(); + // The ChangeRequest will have its own rdf:type from OslcResourceShape describes, + // but no extra types should appear beyond that + } +} diff --git a/OSLC4Net_SDK/Tests/OSLC4Net.Core.Tests/ResourceShapeFactoryTests.cs b/OSLC4Net_SDK/Tests/OSLC4Net.Core.Tests/ResourceShapeFactoryTests.cs index 0c406de8..69c007a1 100644 --- a/OSLC4Net_SDK/Tests/OSLC4Net.Core.Tests/ResourceShapeFactoryTests.cs +++ b/OSLC4Net_SDK/Tests/OSLC4Net.Core.Tests/ResourceShapeFactoryTests.cs @@ -116,7 +116,7 @@ public async Task CreateResourceShape_WithRequirementType_ShouldHaveTypeProperty } [Test] - public async Task CreateResourceShape_WithRequirementType_ShouldOnlyHaveGetterMethods() + public async Task CreateResourceShape_WithRequirementType_ShouldHaveMethodAndPropertyAnnotations() { // Arrange var resourceType = typeof(Requirement); @@ -131,8 +131,10 @@ public async Task CreateResourceShape_WithRequirementType_ShouldOnlyHaveGetterMe // Assert var properties = resourceShape.GetProperties(); - await Assert.That(properties.Count).IsEqualTo(1); - await Assert.That(properties[0].GetName()).IsEqualTo("type"); + // Requirement has both Get* methods and C# properties with OSLC annotations + // The Types property from AbstractResourceRecord is also discovered + await Assert.That(properties.Length).IsGreaterThan(1); + await Assert.That(properties.Any(p => p.GetName() == "type")).IsTrue(); } [Test] @@ -315,21 +317,22 @@ public async Task CreateResourceShape_WithISetUriProperty_ShouldMapToResourceVal var properties = resourceShape.GetProperties(); var uriSetProperty = properties.FirstOrDefault(p => p.GetName() == "uriSet"); - await Assert.That(uriSetProperty).IsNull(); - //Assert.Equal("uriSet", uriSetProperty.GetName()); + // ISet properties are discovered via property scanning + await Assert.That(uriSetProperty).IsNotNull(); + await Assert.That(uriSetProperty.GetName()).IsEqualTo("uriSet"); - //var actualValueType = uriSetProperty.GetValueType(); - //var actualOccurs = uriSetProperty.GetOccurs(); + var actualValueType = uriSetProperty.GetValueType(); + var actualOccurs = uriSetProperty.GetOccurs(); - //Assert.NotNull(actualValueType); - //Assert.NotNull(actualOccurs); + await Assert.That(actualValueType).IsNotNull(); + await Assert.That(actualOccurs).IsNotNull(); - //var expectedValueTypeUri = new Uri(ValueTypeExtension.ToString(OSLC4Net.Core.Model.ValueType.Resource)); - //var expectedOccursUri = new Uri(OccursExtension.ToString(OSLC4Net.Core.Model.Occurs.ZeroOrMany)); + var expectedValueTypeUri = new Uri(ValueTypeExtension.ToString(OSLC4Net.Core.Model.ValueType.Resource)); + var expectedOccursUri = new Uri(OccursExtension.ToString(OSLC4Net.Core.Model.Occurs.ZeroOrMany)); - //Assert.Equal(expectedValueTypeUri, actualValueType); - //Assert.Equal(expectedOccursUri, actualOccurs); - //Assert.Equal("http://example.com/uriSet", uriSetProperty.GetPropertyDefinition()?.ToString()); + await Assert.That(actualValueType).IsEqualTo(expectedValueTypeUri); + await Assert.That(actualOccurs).IsEqualTo(expectedOccursUri); + await Assert.That(uriSetProperty.GetPropertyDefinition()?.ToString()).IsEqualTo("http://example.com/uriSet"); } } From 1b60c4dc8f536a5195e521b28d9c3bb361d7fe42 Mon Sep 17 00:00:00 2001 From: Andrew Berezovskyi Date: Sat, 14 Feb 2026 23:13:08 +0100 Subject: [PATCH 5/5] fix: setter visibility --- OSLC4Net_SDK/OSLC4Net.Core/Model/AbstractResourceRecord.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OSLC4Net_SDK/OSLC4Net.Core/Model/AbstractResourceRecord.cs b/OSLC4Net_SDK/OSLC4Net.Core/Model/AbstractResourceRecord.cs index 775ab223..5386dc56 100644 --- a/OSLC4Net_SDK/OSLC4Net.Core/Model/AbstractResourceRecord.cs +++ b/OSLC4Net_SDK/OSLC4Net.Core/Model/AbstractResourceRecord.cs @@ -21,7 +21,7 @@ public abstract record AbstractResourceRecord : IExtendedResource [OslcName("type")] [OslcPropertyDefinition(OslcConstants.RDF_NAMESPACE + "type")] [OslcTitle("Types")] - public List Types { get; private set; } = new(); + public List Types { get; set; } = new(); ICollection IExtendedResource.Types {