feat: Complete software compilation refactor across client, UI, and importer

Continues the prior commit (model/migrations/server) by finishing every
remaining consumer of the SoftwareCompilation entity introduced there.

- Regenerate Marechai.ApiClient from the updated OpenAPI schema: new
  SoftwareCompilations request builders and DTOs
  (SoftwareCompilationDto, SoftwareCompilationSuccessorDto,
  SoftwareBySoftwareCompilationDto, SoftwareVersionBySoftwareCompilationDto),
  SoftwareReleaseDto's IsCompilation replaced with SoftwareCompilationId/
  SoftwareCompilation. Commit the openapi.yaml snapshot used to drive the
  regeneration.

- Marechai (Blazor): add SoftwareCompilationsService and a full admin UI
  for the new entity (SoftwareCompilations grid + SoftwareCompilationDialog
  with bundled-software/machine pickers, predecessor/relationship type,
  and tabs for included software, included versions, sub-compilations,
  and releases) plus a public detail page at /software-compilation/{id}.
  SoftwareReleaseDialog drops the old IsCompilation checkbox and per-release
  included-items management in favor of a SoftwareCompilation autocomplete
  picker, since compilation membership now lives on the compilation entity
  itself rather than on individual releases. Updated every remaining
  IsCompilation/old-junction-DTO reference in release dialogs, the public
  release/software view pages, and the suggestion dialog. Search.razor now
  routes compilation rows to /software-compilation/{id} instead of a release.

- Marechai.App (Uno): mechanical fixes for the removed IsCompilation
  property and old junction DTOs, and the same scope cut as the Blazor
  app — included-items management removed from the release editor and
  read-only release view (that UI now belongs solely on the compilation),
  with the now-dead helper methods and XAML sections removed.

- Marechai.MobyGames: CompilationRelationService and ImportService now
  create a real SoftwareCompilation row instead of flipping a release flag,
  and resolve each contained slug to either a Software or an existing
  SoftwareCompilation. This fixes a long-standing bug where a slug
  previously converted into a compilation had no replacement pointer
  recorded on MobyGamesImportState, so re-encountering it as a nested
  member of another compilation could never resolve. Nested compilations
  are now linked via SoftwareCompilationBySoftwareCompilation.
  StateService.MarkImportedAsync gained a softwareCompilationId parameter,
  and OrphanCleanupService's software-merge dedup pass was re-keyed from
  the old release-keyed junction to SoftwareBySoftwareCompilation.

Full solution build is clean with no pending EF model changes against the
three migrations from the prior commit.
This commit is contained in:
2026-06-26 04:54:35 +01:00
parent e399e14d38
commit effa7b6791
51 changed files with 77686 additions and 912 deletions

View File

@@ -50,6 +50,7 @@ using Marechai.ApiClient.Search;
using Marechai.ApiClient.Sitemap;
using Marechai.ApiClient.Smartphones;
using Marechai.ApiClient.Software;
using Marechai.ApiClient.SoftwareCompilations;
using Marechai.ApiClient.SoftwarePlatformsByMachine;
using Marechai.ApiClient.SoundSynths;
using Marechai.ApiClient.SoundSynthsByMachine;
@@ -327,6 +328,11 @@ namespace Marechai.ApiClient
{
get => new global::Marechai.ApiClient.Software.SoftwareRequestBuilder(PathParameters, RequestAdapter);
}
/// <summary>The softwareCompilations property</summary>
public global::Marechai.ApiClient.SoftwareCompilations.SoftwareCompilationsRequestBuilder SoftwareCompilations
{
get => new global::Marechai.ApiClient.SoftwareCompilations.SoftwareCompilationsRequestBuilder(PathParameters, RequestAdapter);
}
/// <summary>The softwarePlatformsByMachine property</summary>
public global::Marechai.ApiClient.SoftwarePlatformsByMachine.SoftwarePlatformsByMachineRequestBuilder SoftwarePlatformsByMachine
{

View File

@@ -0,0 +1,73 @@
// <auto-generated/>
#pragma warning disable CS0618
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using System.Collections.Generic;
using System.IO;
using System;
namespace Marechai.ApiClient.Models
{
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
#pragma warning disable CS1591
public partial class SoftwareBySoftwareCompilationDto : IAdditionalDataHolder, IParsable
#pragma warning restore CS1591
{
/// <summary>Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well.</summary>
public IDictionary<string, object> AdditionalData { get; set; }
/// <summary>The software_compilation_id property</summary>
public int? SoftwareCompilationId { get; set; }
/// <summary>The software_id property</summary>
public int? SoftwareId { get; set; }
/// <summary>The software_name property</summary>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public string? SoftwareName { get; set; }
#nullable restore
#else
public string SoftwareName { get; set; }
#endif
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.Models.SoftwareBySoftwareCompilationDto"/> and sets the default values.
/// </summary>
public SoftwareBySoftwareCompilationDto()
{
AdditionalData = new Dictionary<string, object>();
}
/// <summary>
/// Creates a new instance of the appropriate class based on discriminator value
/// </summary>
/// <returns>A <see cref="global::Marechai.ApiClient.Models.SoftwareBySoftwareCompilationDto"/></returns>
/// <param name="parseNode">The parse node to use to read the discriminator value and create the object</param>
public static global::Marechai.ApiClient.Models.SoftwareBySoftwareCompilationDto CreateFromDiscriminatorValue(IParseNode parseNode)
{
if(ReferenceEquals(parseNode, null)) throw new ArgumentNullException(nameof(parseNode));
return new global::Marechai.ApiClient.Models.SoftwareBySoftwareCompilationDto();
}
/// <summary>
/// The deserialization information for the current model
/// </summary>
/// <returns>A IDictionary&lt;string, Action&lt;IParseNode&gt;&gt;</returns>
public virtual IDictionary<string, Action<IParseNode>> GetFieldDeserializers()
{
return new Dictionary<string, Action<IParseNode>>
{
{ "software_compilation_id", n => { SoftwareCompilationId = n.GetIntValue(); } },
{ "software_id", n => { SoftwareId = n.GetIntValue(); } },
{ "software_name", n => { SoftwareName = n.GetStringValue(); } },
};
}
/// <summary>
/// Serializes information the current object
/// </summary>
/// <param name="writer">Serialization writer to use to serialize this model</param>
public virtual void Serialize(ISerializationWriter writer)
{
if(ReferenceEquals(writer, null)) throw new ArgumentNullException(nameof(writer));
writer.WriteIntValue("software_compilation_id", SoftwareCompilationId);
writer.WriteIntValue("software_id", SoftwareId);
writer.WriteStringValue("software_name", SoftwareName);
writer.WriteAdditionalData(AdditionalData);
}
}
}
#pragma warning restore CS0618

View File

@@ -0,0 +1,129 @@
// <auto-generated/>
#pragma warning disable CS0618
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using System.Collections.Generic;
using System.IO;
using System;
namespace Marechai.ApiClient.Models
{
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
#pragma warning disable CS1591
public partial class SoftwareCompilationDto : IAdditionalDataHolder, IParsable
#pragma warning restore CS1591
{
/// <summary>Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well.</summary>
public IDictionary<string, object> AdditionalData { get; set; }
/// <summary>The front_cover_id property</summary>
public Guid? FrontCoverId { get; set; }
/// <summary>The id property</summary>
public int? Id { get; set; }
/// <summary>The machine property</summary>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public string? Machine { get; set; }
#nullable restore
#else
public string Machine { get; set; }
#endif
/// <summary>The machine_id property</summary>
public int? MachineId { get; set; }
/// <summary>The name property</summary>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public string? Name { get; set; }
#nullable restore
#else
public string Name { get; set; }
#endif
/// <summary>The predecessor property</summary>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public string? Predecessor { get; set; }
#nullable restore
#else
public string Predecessor { get; set; }
#endif
/// <summary>The predecessor_id property</summary>
public int? PredecessorId { get; set; }
/// <summary>The relationship_type property</summary>
public int? RelationshipType { get; set; }
/// <summary>The software property</summary>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public string? Software { get; set; }
#nullable restore
#else
public string Software { get; set; }
#endif
/// <summary>The software_id property</summary>
public int? SoftwareId { get; set; }
/// <summary>The successors property</summary>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public List<global::Marechai.ApiClient.Models.SoftwareCompilationSuccessorDto>? Successors { get; set; }
#nullable restore
#else
public List<global::Marechai.ApiClient.Models.SoftwareCompilationSuccessorDto> Successors { get; set; }
#endif
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.Models.SoftwareCompilationDto"/> and sets the default values.
/// </summary>
public SoftwareCompilationDto()
{
AdditionalData = new Dictionary<string, object>();
}
/// <summary>
/// Creates a new instance of the appropriate class based on discriminator value
/// </summary>
/// <returns>A <see cref="global::Marechai.ApiClient.Models.SoftwareCompilationDto"/></returns>
/// <param name="parseNode">The parse node to use to read the discriminator value and create the object</param>
public static global::Marechai.ApiClient.Models.SoftwareCompilationDto CreateFromDiscriminatorValue(IParseNode parseNode)
{
if(ReferenceEquals(parseNode, null)) throw new ArgumentNullException(nameof(parseNode));
return new global::Marechai.ApiClient.Models.SoftwareCompilationDto();
}
/// <summary>
/// The deserialization information for the current model
/// </summary>
/// <returns>A IDictionary&lt;string, Action&lt;IParseNode&gt;&gt;</returns>
public virtual IDictionary<string, Action<IParseNode>> GetFieldDeserializers()
{
return new Dictionary<string, Action<IParseNode>>
{
{ "front_cover_id", n => { FrontCoverId = n.GetGuidValue(); } },
{ "id", n => { Id = n.GetIntValue(); } },
{ "machine", n => { Machine = n.GetStringValue(); } },
{ "machine_id", n => { MachineId = n.GetIntValue(); } },
{ "name", n => { Name = n.GetStringValue(); } },
{ "predecessor", n => { Predecessor = n.GetStringValue(); } },
{ "predecessor_id", n => { PredecessorId = n.GetIntValue(); } },
{ "relationship_type", n => { RelationshipType = n.GetIntValue(); } },
{ "software", n => { Software = n.GetStringValue(); } },
{ "software_id", n => { SoftwareId = n.GetIntValue(); } },
{ "successors", n => { Successors = n.GetCollectionOfObjectValues<global::Marechai.ApiClient.Models.SoftwareCompilationSuccessorDto>(global::Marechai.ApiClient.Models.SoftwareCompilationSuccessorDto.CreateFromDiscriminatorValue)?.AsList(); } },
};
}
/// <summary>
/// Serializes information the current object
/// </summary>
/// <param name="writer">Serialization writer to use to serialize this model</param>
public virtual void Serialize(ISerializationWriter writer)
{
if(ReferenceEquals(writer, null)) throw new ArgumentNullException(nameof(writer));
writer.WriteGuidValue("front_cover_id", FrontCoverId);
writer.WriteIntValue("id", Id);
writer.WriteStringValue("machine", Machine);
writer.WriteIntValue("machine_id", MachineId);
writer.WriteStringValue("name", Name);
writer.WriteStringValue("predecessor", Predecessor);
writer.WriteIntValue("predecessor_id", PredecessorId);
writer.WriteIntValue("relationship_type", RelationshipType);
writer.WriteStringValue("software", Software);
writer.WriteIntValue("software_id", SoftwareId);
writer.WriteCollectionOfObjectValues<global::Marechai.ApiClient.Models.SoftwareCompilationSuccessorDto>("successors", Successors);
writer.WriteAdditionalData(AdditionalData);
}
}
}
#pragma warning restore CS0618

View File

@@ -0,0 +1,73 @@
// <auto-generated/>
#pragma warning disable CS0618
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using System.Collections.Generic;
using System.IO;
using System;
namespace Marechai.ApiClient.Models
{
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
#pragma warning disable CS1591
public partial class SoftwareCompilationSuccessorDto : IAdditionalDataHolder, IParsable
#pragma warning restore CS1591
{
/// <summary>Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well.</summary>
public IDictionary<string, object> AdditionalData { get; set; }
/// <summary>The id property</summary>
public int? Id { get; set; }
/// <summary>The name property</summary>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public string? Name { get; set; }
#nullable restore
#else
public string Name { get; set; }
#endif
/// <summary>The relationship_type property</summary>
public int? RelationshipType { get; set; }
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.Models.SoftwareCompilationSuccessorDto"/> and sets the default values.
/// </summary>
public SoftwareCompilationSuccessorDto()
{
AdditionalData = new Dictionary<string, object>();
}
/// <summary>
/// Creates a new instance of the appropriate class based on discriminator value
/// </summary>
/// <returns>A <see cref="global::Marechai.ApiClient.Models.SoftwareCompilationSuccessorDto"/></returns>
/// <param name="parseNode">The parse node to use to read the discriminator value and create the object</param>
public static global::Marechai.ApiClient.Models.SoftwareCompilationSuccessorDto CreateFromDiscriminatorValue(IParseNode parseNode)
{
if(ReferenceEquals(parseNode, null)) throw new ArgumentNullException(nameof(parseNode));
return new global::Marechai.ApiClient.Models.SoftwareCompilationSuccessorDto();
}
/// <summary>
/// The deserialization information for the current model
/// </summary>
/// <returns>A IDictionary&lt;string, Action&lt;IParseNode&gt;&gt;</returns>
public virtual IDictionary<string, Action<IParseNode>> GetFieldDeserializers()
{
return new Dictionary<string, Action<IParseNode>>
{
{ "id", n => { Id = n.GetIntValue(); } },
{ "name", n => { Name = n.GetStringValue(); } },
{ "relationship_type", n => { RelationshipType = n.GetIntValue(); } },
};
}
/// <summary>
/// Serializes information the current object
/// </summary>
/// <param name="writer">Serialization writer to use to serialize this model</param>
public virtual void Serialize(ISerializationWriter writer)
{
if(ReferenceEquals(writer, null)) throw new ArgumentNullException(nameof(writer));
writer.WriteIntValue("id", Id);
writer.WriteStringValue("name", Name);
writer.WriteIntValue("relationship_type", RelationshipType);
writer.WriteAdditionalData(AdditionalData);
}
}
}
#pragma warning restore CS0618

View File

@@ -16,8 +16,6 @@ namespace Marechai.ApiClient.Models
public IDictionary<string, object> AdditionalData { get; set; }
/// <summary>The id property</summary>
public int? Id { get; set; }
/// <summary>The is_compilation property</summary>
public bool? IsCompilation { get; set; }
/// <summary>The languages property</summary>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
@@ -66,6 +64,16 @@ namespace Marechai.ApiClient.Models
#else
public string Software { get; set; }
#endif
/// <summary>The software_compilation property</summary>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public string? SoftwareCompilation { get; set; }
#nullable restore
#else
public string SoftwareCompilation { get; set; }
#endif
/// <summary>The software_compilation_id property</summary>
public int? SoftwareCompilationId { get; set; }
/// <summary>The software_id property</summary>
public int? SoftwareId { get; set; }
/// <summary>The software_version property</summary>
@@ -112,7 +120,6 @@ namespace Marechai.ApiClient.Models
return new Dictionary<string, Action<IParseNode>>
{
{ "id", n => { Id = n.GetIntValue(); } },
{ "is_compilation", n => { IsCompilation = n.GetBoolValue(); } },
{ "languages", n => { Languages = n.GetCollectionOfObjectValues<global::Marechai.ApiClient.Models.LanguageBySoftwareReleaseDto>(global::Marechai.ApiClient.Models.LanguageBySoftwareReleaseDto.CreateFromDiscriminatorValue)?.AsList(); } },
{ "platform", n => { Platform = n.GetStringValue(); } },
{ "platform_id", n => { PlatformId = n.GetIntValue(); } },
@@ -122,6 +129,8 @@ namespace Marechai.ApiClient.Models
{ "release_date", n => { ReleaseDate = n.GetDateTimeOffsetValue(); } },
{ "release_date_precision", n => { ReleaseDatePrecision = n.GetIntValue(); } },
{ "software", n => { Software = n.GetStringValue(); } },
{ "software_compilation", n => { SoftwareCompilation = n.GetStringValue(); } },
{ "software_compilation_id", n => { SoftwareCompilationId = n.GetIntValue(); } },
{ "software_id", n => { SoftwareId = n.GetIntValue(); } },
{ "software_version", n => { SoftwareVersion = n.GetStringValue(); } },
{ "software_version_id", n => { SoftwareVersionId = n.GetIntValue(); } },
@@ -136,7 +145,6 @@ namespace Marechai.ApiClient.Models
{
if(ReferenceEquals(writer, null)) throw new ArgumentNullException(nameof(writer));
writer.WriteIntValue("id", Id);
writer.WriteBoolValue("is_compilation", IsCompilation);
writer.WriteCollectionOfObjectValues<global::Marechai.ApiClient.Models.LanguageBySoftwareReleaseDto>("languages", Languages);
writer.WriteStringValue("platform", Platform);
writer.WriteIntValue("platform_id", PlatformId);
@@ -146,6 +154,8 @@ namespace Marechai.ApiClient.Models
writer.WriteDateTimeOffsetValue("release_date", ReleaseDate);
writer.WriteIntValue("release_date_precision", ReleaseDatePrecision);
writer.WriteStringValue("software", Software);
writer.WriteStringValue("software_compilation", SoftwareCompilation);
writer.WriteIntValue("software_compilation_id", SoftwareCompilationId);
writer.WriteIntValue("software_id", SoftwareId);
writer.WriteStringValue("software_version", SoftwareVersion);
writer.WriteIntValue("software_version_id", SoftwareVersionId);

View File

@@ -0,0 +1,83 @@
// <auto-generated/>
#pragma warning disable CS0618
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using System.Collections.Generic;
using System.IO;
using System;
namespace Marechai.ApiClient.Models
{
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
#pragma warning disable CS1591
public partial class SoftwareVersionBySoftwareCompilationDto : IAdditionalDataHolder, IParsable
#pragma warning restore CS1591
{
/// <summary>Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well.</summary>
public IDictionary<string, object> AdditionalData { get; set; }
/// <summary>The software_compilation_id property</summary>
public int? SoftwareCompilationId { get; set; }
/// <summary>The software_name property</summary>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public string? SoftwareName { get; set; }
#nullable restore
#else
public string SoftwareName { get; set; }
#endif
/// <summary>The software_version property</summary>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public string? SoftwareVersion { get; set; }
#nullable restore
#else
public string SoftwareVersion { get; set; }
#endif
/// <summary>The software_version_id property</summary>
public int? SoftwareVersionId { get; set; }
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.Models.SoftwareVersionBySoftwareCompilationDto"/> and sets the default values.
/// </summary>
public SoftwareVersionBySoftwareCompilationDto()
{
AdditionalData = new Dictionary<string, object>();
}
/// <summary>
/// Creates a new instance of the appropriate class based on discriminator value
/// </summary>
/// <returns>A <see cref="global::Marechai.ApiClient.Models.SoftwareVersionBySoftwareCompilationDto"/></returns>
/// <param name="parseNode">The parse node to use to read the discriminator value and create the object</param>
public static global::Marechai.ApiClient.Models.SoftwareVersionBySoftwareCompilationDto CreateFromDiscriminatorValue(IParseNode parseNode)
{
if(ReferenceEquals(parseNode, null)) throw new ArgumentNullException(nameof(parseNode));
return new global::Marechai.ApiClient.Models.SoftwareVersionBySoftwareCompilationDto();
}
/// <summary>
/// The deserialization information for the current model
/// </summary>
/// <returns>A IDictionary&lt;string, Action&lt;IParseNode&gt;&gt;</returns>
public virtual IDictionary<string, Action<IParseNode>> GetFieldDeserializers()
{
return new Dictionary<string, Action<IParseNode>>
{
{ "software_compilation_id", n => { SoftwareCompilationId = n.GetIntValue(); } },
{ "software_name", n => { SoftwareName = n.GetStringValue(); } },
{ "software_version", n => { SoftwareVersion = n.GetStringValue(); } },
{ "software_version_id", n => { SoftwareVersionId = n.GetIntValue(); } },
};
}
/// <summary>
/// Serializes information the current object
/// </summary>
/// <param name="writer">Serialization writer to use to serialize this model</param>
public virtual void Serialize(ISerializationWriter writer)
{
if(ReferenceEquals(writer, null)) throw new ArgumentNullException(nameof(writer));
writer.WriteIntValue("software_compilation_id", SoftwareCompilationId);
writer.WriteStringValue("software_name", SoftwareName);
writer.WriteStringValue("software_version", SoftwareVersion);
writer.WriteIntValue("software_version_id", SoftwareVersionId);
writer.WriteAdditionalData(AdditionalData);
}
}
}
#pragma warning restore CS0618

View File

@@ -0,0 +1,100 @@
// <auto-generated/>
#pragma warning disable CS0618
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System;
namespace Marechai.ApiClient.SoftwareCompilations.Count
{
/// <summary>
/// Builds and executes requests for operations under \software-compilations\count
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class CountRequestBuilder : BaseRequestBuilder
{
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.SoftwareCompilations.Count.CountRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public CountRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software-compilations/count{?search*}", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.SoftwareCompilations.Count.CountRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public CountRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software-compilations/count{?search*}", rawUrl)
{
}
/// <returns>A <see cref="int"/></returns>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<int?> GetAsync(Action<RequestConfiguration<global::Marechai.ApiClient.SoftwareCompilations.Count.CountRequestBuilder.CountRequestBuilderGetQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<int?> GetAsync(Action<RequestConfiguration<global::Marechai.ApiClient.SoftwareCompilations.Count.CountRequestBuilder.CountRequestBuilderGetQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToGetRequestInformation(requestConfiguration);
return await RequestAdapter.SendPrimitiveAsync<int?>(requestInfo, default, cancellationToken).ConfigureAwait(false);
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<global::Marechai.ApiClient.SoftwareCompilations.Count.CountRequestBuilder.CountRequestBuilderGetQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<global::Marechai.ApiClient.SoftwareCompilations.Count.CountRequestBuilder.CountRequestBuilderGetQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
return requestInfo;
}
/// <summary>
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
/// </summary>
/// <returns>A <see cref="global::Marechai.ApiClient.SoftwareCompilations.Count.CountRequestBuilder"/></returns>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
public global::Marechai.ApiClient.SoftwareCompilations.Count.CountRequestBuilder WithUrl(string rawUrl)
{
return new global::Marechai.ApiClient.SoftwareCompilations.Count.CountRequestBuilder(rawUrl, RequestAdapter);
}
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
#pragma warning disable CS1591
public partial class CountRequestBuilderGetQueryParameters
#pragma warning restore CS1591
{
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
[QueryParameter("search")]
public string? Search { get; set; }
#nullable restore
#else
[QueryParameter("search")]
public string Search { get; set; }
#endif
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class CountRequestBuilderGetRequestConfiguration : RequestConfiguration<global::Marechai.ApiClient.SoftwareCompilations.Count.CountRequestBuilder.CountRequestBuilderGetQueryParameters>
{
}
}
}
#pragma warning restore CS0618

View File

@@ -0,0 +1,113 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.ApiClient.Models;
using Marechai.ApiClient.SoftwareCompilations.Item.Compilations.Item;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System;
namespace Marechai.ApiClient.SoftwareCompilations.Item.Compilations
{
/// <summary>
/// Builds and executes requests for operations under \software-compilations\{id}\compilations
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class CompilationsRequestBuilder : BaseRequestBuilder
{
/// <summary>Gets an item from the Marechai.ApiClient.softwareCompilations.item.compilations.item collection</summary>
/// <param name="position">Unique identifier of the item</param>
/// <returns>A <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Compilations.Item.WithChildItemRequestBuilder"/></returns>
public global::Marechai.ApiClient.SoftwareCompilations.Item.Compilations.Item.WithChildItemRequestBuilder this[int position]
{
get
{
var urlTplParams = new Dictionary<string, object>(PathParameters);
urlTplParams.Add("childId", position);
return new global::Marechai.ApiClient.SoftwareCompilations.Item.Compilations.Item.WithChildItemRequestBuilder(urlTplParams, RequestAdapter);
}
}
/// <summary>Gets an item from the Marechai.ApiClient.softwareCompilations.item.compilations.item collection</summary>
/// <param name="position">Unique identifier of the item</param>
/// <returns>A <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Compilations.Item.WithChildItemRequestBuilder"/></returns>
[Obsolete("This indexer is deprecated and will be removed in the next major version. Use the one with the typed parameter instead.")]
public global::Marechai.ApiClient.SoftwareCompilations.Item.Compilations.Item.WithChildItemRequestBuilder this[string position]
{
get
{
var urlTplParams = new Dictionary<string, object>(PathParameters);
if (!string.IsNullOrWhiteSpace(position)) urlTplParams.Add("childId", position);
return new global::Marechai.ApiClient.SoftwareCompilations.Item.Compilations.Item.WithChildItemRequestBuilder(urlTplParams, RequestAdapter);
}
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Compilations.CompilationsRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public CompilationsRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software-compilations/{id}/compilations", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Compilations.CompilationsRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public CompilationsRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software-compilations/{id}/compilations", rawUrl)
{
}
/// <returns>A List&lt;global::Marechai.ApiClient.Models.SoftwareCompilationDto&gt;</returns>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<List<global::Marechai.ApiClient.Models.SoftwareCompilationDto>?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<List<global::Marechai.ApiClient.Models.SoftwareCompilationDto>> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToGetRequestInformation(requestConfiguration);
var collectionResult = await RequestAdapter.SendCollectionAsync<global::Marechai.ApiClient.Models.SoftwareCompilationDto>(requestInfo, global::Marechai.ApiClient.Models.SoftwareCompilationDto.CreateFromDiscriminatorValue, default, cancellationToken).ConfigureAwait(false);
return collectionResult?.AsList();
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
return requestInfo;
}
/// <summary>
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
/// </summary>
/// <returns>A <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Compilations.CompilationsRequestBuilder"/></returns>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
public global::Marechai.ApiClient.SoftwareCompilations.Item.Compilations.CompilationsRequestBuilder WithUrl(string rawUrl)
{
return new global::Marechai.ApiClient.SoftwareCompilations.Item.Compilations.CompilationsRequestBuilder(rawUrl, RequestAdapter);
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class CompilationsRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
}
}
#pragma warning restore CS0618

View File

@@ -0,0 +1,141 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.ApiClient.Models;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System;
namespace Marechai.ApiClient.SoftwareCompilations.Item.Compilations.Item
{
/// <summary>
/// Builds and executes requests for operations under \software-compilations\{id}\compilations\{childId}
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class WithChildItemRequestBuilder : BaseRequestBuilder
{
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Compilations.Item.WithChildItemRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public WithChildItemRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software-compilations/{id}/compilations/{childId}", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Compilations.Item.WithChildItemRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public WithChildItemRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software-compilations/{id}/compilations/{childId}", rawUrl)
{
}
/// <returns>A <see cref="Stream"/></returns>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 401 status code</exception>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 404 status code</exception>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<Stream?> DeleteAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<Stream> DeleteAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToDeleteRequestInformation(requestConfiguration);
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
{
{ "401", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
{ "404", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
};
return await RequestAdapter.SendPrimitiveAsync<Stream>(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
}
/// <returns>A <see cref="Stream"/></returns>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 400 status code</exception>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 401 status code</exception>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 404 status code</exception>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<Stream?> PostAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<Stream> PostAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToPostRequestInformation(requestConfiguration);
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
{
{ "400", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
{ "401", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
{ "404", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
};
return await RequestAdapter.SendPrimitiveAsync<Stream>(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToDeleteRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToDeleteRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.DELETE, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "application/json, text/plain;q=0.9");
return requestInfo;
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToPostRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToPostRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.POST, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "application/json, text/plain;q=0.9");
return requestInfo;
}
/// <summary>
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
/// </summary>
/// <returns>A <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Compilations.Item.WithChildItemRequestBuilder"/></returns>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
public global::Marechai.ApiClient.SoftwareCompilations.Item.Compilations.Item.WithChildItemRequestBuilder WithUrl(string rawUrl)
{
return new global::Marechai.ApiClient.SoftwareCompilations.Item.Compilations.Item.WithChildItemRequestBuilder(rawUrl, RequestAdapter);
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class WithChildItemRequestBuilderDeleteRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class WithChildItemRequestBuilderPostRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
}
}
#pragma warning restore CS0618

View File

@@ -0,0 +1,87 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.ApiClient.Models;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System;
namespace Marechai.ApiClient.SoftwareCompilations.Item.Covers
{
/// <summary>
/// Builds and executes requests for operations under \software-compilations\{id}\covers
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class CoversRequestBuilder : BaseRequestBuilder
{
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Covers.CoversRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public CoversRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software-compilations/{id}/covers", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Covers.CoversRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public CoversRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software-compilations/{id}/covers", rawUrl)
{
}
/// <returns>A List&lt;global::Marechai.ApiClient.Models.SoftwareCoverDto&gt;</returns>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<List<global::Marechai.ApiClient.Models.SoftwareCoverDto>?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<List<global::Marechai.ApiClient.Models.SoftwareCoverDto>> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToGetRequestInformation(requestConfiguration);
var collectionResult = await RequestAdapter.SendCollectionAsync<global::Marechai.ApiClient.Models.SoftwareCoverDto>(requestInfo, global::Marechai.ApiClient.Models.SoftwareCoverDto.CreateFromDiscriminatorValue, default, cancellationToken).ConfigureAwait(false);
return collectionResult?.AsList();
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
return requestInfo;
}
/// <summary>
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
/// </summary>
/// <returns>A <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Covers.CoversRequestBuilder"/></returns>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
public global::Marechai.ApiClient.SoftwareCompilations.Item.Covers.CoversRequestBuilder WithUrl(string rawUrl)
{
return new global::Marechai.ApiClient.SoftwareCompilations.Item.Covers.CoversRequestBuilder(rawUrl, RequestAdapter);
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class CoversRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
}
}
#pragma warning restore CS0618

View File

@@ -0,0 +1,93 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.ApiClient.Models;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System;
namespace Marechai.ApiClient.SoftwareCompilations.Item.Software.Item
{
/// <summary>
/// Builds and executes requests for operations under \software-compilations\{id}\software\{softwareId}
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class WithSoftwareItemRequestBuilder : BaseRequestBuilder
{
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Software.Item.WithSoftwareItemRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public WithSoftwareItemRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software-compilations/{id}/software/{softwareId}", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Software.Item.WithSoftwareItemRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public WithSoftwareItemRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software-compilations/{id}/software/{softwareId}", rawUrl)
{
}
/// <returns>A <see cref="Stream"/></returns>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 401 status code</exception>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 404 status code</exception>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<Stream?> DeleteAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<Stream> DeleteAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToDeleteRequestInformation(requestConfiguration);
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
{
{ "401", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
{ "404", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
};
return await RequestAdapter.SendPrimitiveAsync<Stream>(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToDeleteRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToDeleteRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.DELETE, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "application/json, text/plain;q=0.9");
return requestInfo;
}
/// <summary>
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
/// </summary>
/// <returns>A <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Software.Item.WithSoftwareItemRequestBuilder"/></returns>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
public global::Marechai.ApiClient.SoftwareCompilations.Item.Software.Item.WithSoftwareItemRequestBuilder WithUrl(string rawUrl)
{
return new global::Marechai.ApiClient.SoftwareCompilations.Item.Software.Item.WithSoftwareItemRequestBuilder(rawUrl, RequestAdapter);
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class WithSoftwareItemRequestBuilderDeleteRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
}
}
#pragma warning restore CS0618

View File

@@ -0,0 +1,166 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.ApiClient.Models;
using Marechai.ApiClient.SoftwareCompilations.Item.Software.Item;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System;
namespace Marechai.ApiClient.SoftwareCompilations.Item.Software
{
/// <summary>
/// Builds and executes requests for operations under \software-compilations\{id}\software
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class SoftwareRequestBuilder : BaseRequestBuilder
{
/// <summary>Gets an item from the Marechai.ApiClient.softwareCompilations.item.software.item collection</summary>
/// <param name="position">Unique identifier of the item</param>
/// <returns>A <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Software.Item.WithSoftwareItemRequestBuilder"/></returns>
public global::Marechai.ApiClient.SoftwareCompilations.Item.Software.Item.WithSoftwareItemRequestBuilder this[int position]
{
get
{
var urlTplParams = new Dictionary<string, object>(PathParameters);
urlTplParams.Add("softwareId", position);
return new global::Marechai.ApiClient.SoftwareCompilations.Item.Software.Item.WithSoftwareItemRequestBuilder(urlTplParams, RequestAdapter);
}
}
/// <summary>Gets an item from the Marechai.ApiClient.softwareCompilations.item.software.item collection</summary>
/// <param name="position">Unique identifier of the item</param>
/// <returns>A <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Software.Item.WithSoftwareItemRequestBuilder"/></returns>
[Obsolete("This indexer is deprecated and will be removed in the next major version. Use the one with the typed parameter instead.")]
public global::Marechai.ApiClient.SoftwareCompilations.Item.Software.Item.WithSoftwareItemRequestBuilder this[string position]
{
get
{
var urlTplParams = new Dictionary<string, object>(PathParameters);
if (!string.IsNullOrWhiteSpace(position)) urlTplParams.Add("softwareId", position);
return new global::Marechai.ApiClient.SoftwareCompilations.Item.Software.Item.WithSoftwareItemRequestBuilder(urlTplParams, RequestAdapter);
}
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Software.SoftwareRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public SoftwareRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software-compilations/{id}/software", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Software.SoftwareRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public SoftwareRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software-compilations/{id}/software", rawUrl)
{
}
/// <returns>A List&lt;global::Marechai.ApiClient.Models.SoftwareBySoftwareCompilationDto&gt;</returns>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<List<global::Marechai.ApiClient.Models.SoftwareBySoftwareCompilationDto>?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<List<global::Marechai.ApiClient.Models.SoftwareBySoftwareCompilationDto>> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToGetRequestInformation(requestConfiguration);
var collectionResult = await RequestAdapter.SendCollectionAsync<global::Marechai.ApiClient.Models.SoftwareBySoftwareCompilationDto>(requestInfo, global::Marechai.ApiClient.Models.SoftwareBySoftwareCompilationDto.CreateFromDiscriminatorValue, default, cancellationToken).ConfigureAwait(false);
return collectionResult?.AsList();
}
/// <returns>A <see cref="Stream"/></returns>
/// <param name="body">The request body</param>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 400 status code</exception>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 401 status code</exception>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 404 status code</exception>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<Stream?> PostAsync(global::Marechai.ApiClient.Models.SoftwareBySoftwareCompilationDto body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<Stream> PostAsync(global::Marechai.ApiClient.Models.SoftwareBySoftwareCompilationDto body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
if(ReferenceEquals(body, null)) throw new ArgumentNullException(nameof(body));
var requestInfo = ToPostRequestInformation(body, requestConfiguration);
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
{
{ "400", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
{ "401", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
{ "404", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
};
return await RequestAdapter.SendPrimitiveAsync<Stream>(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
return requestInfo;
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="body">The request body</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToPostRequestInformation(global::Marechai.ApiClient.Models.SoftwareBySoftwareCompilationDto body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToPostRequestInformation(global::Marechai.ApiClient.Models.SoftwareBySoftwareCompilationDto body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
if(ReferenceEquals(body, null)) throw new ArgumentNullException(nameof(body));
var requestInfo = new RequestInformation(Method.POST, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "application/json, text/plain;q=0.9");
requestInfo.SetContentFromParsable(RequestAdapter, "application/json", body);
return requestInfo;
}
/// <summary>
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
/// </summary>
/// <returns>A <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Software.SoftwareRequestBuilder"/></returns>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
public global::Marechai.ApiClient.SoftwareCompilations.Item.Software.SoftwareRequestBuilder WithUrl(string rawUrl)
{
return new global::Marechai.ApiClient.SoftwareCompilations.Item.Software.SoftwareRequestBuilder(rawUrl, RequestAdapter);
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class SoftwareRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class SoftwareRequestBuilderPostRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
}
}
#pragma warning restore CS0618

View File

@@ -0,0 +1,215 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.ApiClient.Models;
using Marechai.ApiClient.SoftwareCompilations.Item.Compilations;
using Marechai.ApiClient.SoftwareCompilations.Item.Covers;
using Marechai.ApiClient.SoftwareCompilations.Item.Releases;
using Marechai.ApiClient.SoftwareCompilations.Item.Software;
using Marechai.ApiClient.SoftwareCompilations.Item.Versions;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System;
namespace Marechai.ApiClient.SoftwareCompilations.Item
{
/// <summary>
/// Builds and executes requests for operations under \software-compilations\{id}
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class SoftwareCompilationsItemRequestBuilder : BaseRequestBuilder
{
/// <summary>The compilations property</summary>
public global::Marechai.ApiClient.SoftwareCompilations.Item.Compilations.CompilationsRequestBuilder Compilations
{
get => new global::Marechai.ApiClient.SoftwareCompilations.Item.Compilations.CompilationsRequestBuilder(PathParameters, RequestAdapter);
}
/// <summary>The covers property</summary>
public global::Marechai.ApiClient.SoftwareCompilations.Item.Covers.CoversRequestBuilder Covers
{
get => new global::Marechai.ApiClient.SoftwareCompilations.Item.Covers.CoversRequestBuilder(PathParameters, RequestAdapter);
}
/// <summary>The releases property</summary>
public global::Marechai.ApiClient.SoftwareCompilations.Item.Releases.ReleasesRequestBuilder Releases
{
get => new global::Marechai.ApiClient.SoftwareCompilations.Item.Releases.ReleasesRequestBuilder(PathParameters, RequestAdapter);
}
/// <summary>The software property</summary>
public global::Marechai.ApiClient.SoftwareCompilations.Item.Software.SoftwareRequestBuilder Software
{
get => new global::Marechai.ApiClient.SoftwareCompilations.Item.Software.SoftwareRequestBuilder(PathParameters, RequestAdapter);
}
/// <summary>The versions property</summary>
public global::Marechai.ApiClient.SoftwareCompilations.Item.Versions.VersionsRequestBuilder Versions
{
get => new global::Marechai.ApiClient.SoftwareCompilations.Item.Versions.VersionsRequestBuilder(PathParameters, RequestAdapter);
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.SoftwareCompilationsItemRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public SoftwareCompilationsItemRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software-compilations/{id}", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.SoftwareCompilationsItemRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public SoftwareCompilationsItemRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software-compilations/{id}", rawUrl)
{
}
/// <returns>A <see cref="Stream"/></returns>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 401 status code</exception>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 404 status code</exception>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<Stream?> DeleteAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<Stream> DeleteAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToDeleteRequestInformation(requestConfiguration);
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
{
{ "401", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
{ "404", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
};
return await RequestAdapter.SendPrimitiveAsync<Stream>(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
}
/// <returns>A <see cref="global::Marechai.ApiClient.Models.SoftwareCompilationDto"/></returns>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<global::Marechai.ApiClient.Models.SoftwareCompilationDto?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<global::Marechai.ApiClient.Models.SoftwareCompilationDto> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToGetRequestInformation(requestConfiguration);
return await RequestAdapter.SendAsync<global::Marechai.ApiClient.Models.SoftwareCompilationDto>(requestInfo, global::Marechai.ApiClient.Models.SoftwareCompilationDto.CreateFromDiscriminatorValue, default, cancellationToken).ConfigureAwait(false);
}
/// <returns>A <see cref="Stream"/></returns>
/// <param name="body">The request body</param>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 400 status code</exception>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 401 status code</exception>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 404 status code</exception>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<Stream?> PutAsync(global::Marechai.ApiClient.Models.SoftwareCompilationDto body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<Stream> PutAsync(global::Marechai.ApiClient.Models.SoftwareCompilationDto body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
if(ReferenceEquals(body, null)) throw new ArgumentNullException(nameof(body));
var requestInfo = ToPutRequestInformation(body, requestConfiguration);
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
{
{ "400", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
{ "401", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
{ "404", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
};
return await RequestAdapter.SendPrimitiveAsync<Stream>(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToDeleteRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToDeleteRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.DELETE, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "application/json, text/plain;q=0.9");
return requestInfo;
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "application/json, text/plain;q=0.9");
return requestInfo;
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="body">The request body</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToPutRequestInformation(global::Marechai.ApiClient.Models.SoftwareCompilationDto body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToPutRequestInformation(global::Marechai.ApiClient.Models.SoftwareCompilationDto body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
if(ReferenceEquals(body, null)) throw new ArgumentNullException(nameof(body));
var requestInfo = new RequestInformation(Method.PUT, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "application/json, text/plain;q=0.9");
requestInfo.SetContentFromParsable(RequestAdapter, "application/json", body);
return requestInfo;
}
/// <summary>
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
/// </summary>
/// <returns>A <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.SoftwareCompilationsItemRequestBuilder"/></returns>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
public global::Marechai.ApiClient.SoftwareCompilations.Item.SoftwareCompilationsItemRequestBuilder WithUrl(string rawUrl)
{
return new global::Marechai.ApiClient.SoftwareCompilations.Item.SoftwareCompilationsItemRequestBuilder(rawUrl, RequestAdapter);
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class SoftwareCompilationsItemRequestBuilderDeleteRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class SoftwareCompilationsItemRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class SoftwareCompilationsItemRequestBuilderPutRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
}
}
#pragma warning restore CS0618

View File

@@ -0,0 +1,93 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.ApiClient.Models;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System;
namespace Marechai.ApiClient.SoftwareCompilations.Item.Versions.Item
{
/// <summary>
/// Builds and executes requests for operations under \software-compilations\{id}\versions\{versionId}
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class WithVersionItemRequestBuilder : BaseRequestBuilder
{
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Versions.Item.WithVersionItemRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public WithVersionItemRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software-compilations/{id}/versions/{versionId}", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Versions.Item.WithVersionItemRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public WithVersionItemRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software-compilations/{id}/versions/{versionId}", rawUrl)
{
}
/// <returns>A <see cref="Stream"/></returns>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 401 status code</exception>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 404 status code</exception>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<Stream?> DeleteAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<Stream> DeleteAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToDeleteRequestInformation(requestConfiguration);
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
{
{ "401", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
{ "404", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
};
return await RequestAdapter.SendPrimitiveAsync<Stream>(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToDeleteRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToDeleteRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.DELETE, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "application/json, text/plain;q=0.9");
return requestInfo;
}
/// <summary>
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
/// </summary>
/// <returns>A <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Versions.Item.WithVersionItemRequestBuilder"/></returns>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
public global::Marechai.ApiClient.SoftwareCompilations.Item.Versions.Item.WithVersionItemRequestBuilder WithUrl(string rawUrl)
{
return new global::Marechai.ApiClient.SoftwareCompilations.Item.Versions.Item.WithVersionItemRequestBuilder(rawUrl, RequestAdapter);
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class WithVersionItemRequestBuilderDeleteRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
}
}
#pragma warning restore CS0618

View File

@@ -0,0 +1,166 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.ApiClient.Models;
using Marechai.ApiClient.SoftwareCompilations.Item.Versions.Item;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System;
namespace Marechai.ApiClient.SoftwareCompilations.Item.Versions
{
/// <summary>
/// Builds and executes requests for operations under \software-compilations\{id}\versions
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class VersionsRequestBuilder : BaseRequestBuilder
{
/// <summary>Gets an item from the Marechai.ApiClient.softwareCompilations.item.versions.item collection</summary>
/// <param name="position">Unique identifier of the item</param>
/// <returns>A <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Versions.Item.WithVersionItemRequestBuilder"/></returns>
public global::Marechai.ApiClient.SoftwareCompilations.Item.Versions.Item.WithVersionItemRequestBuilder this[int position]
{
get
{
var urlTplParams = new Dictionary<string, object>(PathParameters);
urlTplParams.Add("versionId", position);
return new global::Marechai.ApiClient.SoftwareCompilations.Item.Versions.Item.WithVersionItemRequestBuilder(urlTplParams, RequestAdapter);
}
}
/// <summary>Gets an item from the Marechai.ApiClient.softwareCompilations.item.versions.item collection</summary>
/// <param name="position">Unique identifier of the item</param>
/// <returns>A <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Versions.Item.WithVersionItemRequestBuilder"/></returns>
[Obsolete("This indexer is deprecated and will be removed in the next major version. Use the one with the typed parameter instead.")]
public global::Marechai.ApiClient.SoftwareCompilations.Item.Versions.Item.WithVersionItemRequestBuilder this[string position]
{
get
{
var urlTplParams = new Dictionary<string, object>(PathParameters);
if (!string.IsNullOrWhiteSpace(position)) urlTplParams.Add("versionId", position);
return new global::Marechai.ApiClient.SoftwareCompilations.Item.Versions.Item.WithVersionItemRequestBuilder(urlTplParams, RequestAdapter);
}
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Versions.VersionsRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public VersionsRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software-compilations/{id}/versions", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Versions.VersionsRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public VersionsRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software-compilations/{id}/versions", rawUrl)
{
}
/// <returns>A List&lt;global::Marechai.ApiClient.Models.SoftwareVersionBySoftwareCompilationDto&gt;</returns>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<List<global::Marechai.ApiClient.Models.SoftwareVersionBySoftwareCompilationDto>?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<List<global::Marechai.ApiClient.Models.SoftwareVersionBySoftwareCompilationDto>> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToGetRequestInformation(requestConfiguration);
var collectionResult = await RequestAdapter.SendCollectionAsync<global::Marechai.ApiClient.Models.SoftwareVersionBySoftwareCompilationDto>(requestInfo, global::Marechai.ApiClient.Models.SoftwareVersionBySoftwareCompilationDto.CreateFromDiscriminatorValue, default, cancellationToken).ConfigureAwait(false);
return collectionResult?.AsList();
}
/// <returns>A <see cref="Stream"/></returns>
/// <param name="body">The request body</param>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 400 status code</exception>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 401 status code</exception>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 404 status code</exception>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<Stream?> PostAsync(global::Marechai.ApiClient.Models.SoftwareVersionBySoftwareCompilationDto body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<Stream> PostAsync(global::Marechai.ApiClient.Models.SoftwareVersionBySoftwareCompilationDto body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
if(ReferenceEquals(body, null)) throw new ArgumentNullException(nameof(body));
var requestInfo = ToPostRequestInformation(body, requestConfiguration);
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
{
{ "400", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
{ "401", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
{ "404", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
};
return await RequestAdapter.SendPrimitiveAsync<Stream>(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
return requestInfo;
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="body">The request body</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToPostRequestInformation(global::Marechai.ApiClient.Models.SoftwareVersionBySoftwareCompilationDto body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToPostRequestInformation(global::Marechai.ApiClient.Models.SoftwareVersionBySoftwareCompilationDto body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
if(ReferenceEquals(body, null)) throw new ArgumentNullException(nameof(body));
var requestInfo = new RequestInformation(Method.POST, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "application/json, text/plain;q=0.9");
requestInfo.SetContentFromParsable(RequestAdapter, "application/json", body);
return requestInfo;
}
/// <summary>
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
/// </summary>
/// <returns>A <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.Versions.VersionsRequestBuilder"/></returns>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
public global::Marechai.ApiClient.SoftwareCompilations.Item.Versions.VersionsRequestBuilder WithUrl(string rawUrl)
{
return new global::Marechai.ApiClient.SoftwareCompilations.Item.Versions.VersionsRequestBuilder(rawUrl, RequestAdapter);
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class VersionsRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class VersionsRequestBuilderPostRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
}
}
#pragma warning restore CS0618

View File

@@ -0,0 +1,189 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.ApiClient.Models;
using Marechai.ApiClient.SoftwareCompilations.Count;
using Marechai.ApiClient.SoftwareCompilations.Item;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System;
namespace Marechai.ApiClient.SoftwareCompilations
{
/// <summary>
/// Builds and executes requests for operations under \software-compilations
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class SoftwareCompilationsRequestBuilder : BaseRequestBuilder
{
/// <summary>The count property</summary>
public global::Marechai.ApiClient.SoftwareCompilations.Count.CountRequestBuilder Count
{
get => new global::Marechai.ApiClient.SoftwareCompilations.Count.CountRequestBuilder(PathParameters, RequestAdapter);
}
/// <summary>Gets an item from the Marechai.ApiClient.softwareCompilations.item collection</summary>
/// <param name="position">Unique identifier of the item</param>
/// <returns>A <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.SoftwareCompilationsItemRequestBuilder"/></returns>
public global::Marechai.ApiClient.SoftwareCompilations.Item.SoftwareCompilationsItemRequestBuilder this[int position]
{
get
{
var urlTplParams = new Dictionary<string, object>(PathParameters);
urlTplParams.Add("id", position);
return new global::Marechai.ApiClient.SoftwareCompilations.Item.SoftwareCompilationsItemRequestBuilder(urlTplParams, RequestAdapter);
}
}
/// <summary>Gets an item from the Marechai.ApiClient.softwareCompilations.item collection</summary>
/// <param name="position">Unique identifier of the item</param>
/// <returns>A <see cref="global::Marechai.ApiClient.SoftwareCompilations.Item.SoftwareCompilationsItemRequestBuilder"/></returns>
[Obsolete("This indexer is deprecated and will be removed in the next major version. Use the one with the typed parameter instead.")]
public global::Marechai.ApiClient.SoftwareCompilations.Item.SoftwareCompilationsItemRequestBuilder this[string position]
{
get
{
var urlTplParams = new Dictionary<string, object>(PathParameters);
if (!string.IsNullOrWhiteSpace(position)) urlTplParams.Add("id", position);
return new global::Marechai.ApiClient.SoftwareCompilations.Item.SoftwareCompilationsItemRequestBuilder(urlTplParams, RequestAdapter);
}
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.SoftwareCompilations.SoftwareCompilationsRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public SoftwareCompilationsRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software-compilations{?search*,skip*,take*}", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.SoftwareCompilations.SoftwareCompilationsRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public SoftwareCompilationsRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/software-compilations{?search*,skip*,take*}", rawUrl)
{
}
/// <returns>A List&lt;global::Marechai.ApiClient.Models.SoftwareCompilationDto&gt;</returns>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<List<global::Marechai.ApiClient.Models.SoftwareCompilationDto>?> GetAsync(Action<RequestConfiguration<global::Marechai.ApiClient.SoftwareCompilations.SoftwareCompilationsRequestBuilder.SoftwareCompilationsRequestBuilderGetQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<List<global::Marechai.ApiClient.Models.SoftwareCompilationDto>> GetAsync(Action<RequestConfiguration<global::Marechai.ApiClient.SoftwareCompilations.SoftwareCompilationsRequestBuilder.SoftwareCompilationsRequestBuilderGetQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToGetRequestInformation(requestConfiguration);
var collectionResult = await RequestAdapter.SendCollectionAsync<global::Marechai.ApiClient.Models.SoftwareCompilationDto>(requestInfo, global::Marechai.ApiClient.Models.SoftwareCompilationDto.CreateFromDiscriminatorValue, default, cancellationToken).ConfigureAwait(false);
return collectionResult?.AsList();
}
/// <returns>A <see cref="int"/></returns>
/// <param name="body">The request body</param>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 400 status code</exception>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 401 status code</exception>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<int?> PostAsync(global::Marechai.ApiClient.Models.SoftwareCompilationDto body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<int?> PostAsync(global::Marechai.ApiClient.Models.SoftwareCompilationDto body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
if(ReferenceEquals(body, null)) throw new ArgumentNullException(nameof(body));
var requestInfo = ToPostRequestInformation(body, requestConfiguration);
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
{
{ "400", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
{ "401", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
};
return await RequestAdapter.SendPrimitiveAsync<int?>(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<global::Marechai.ApiClient.SoftwareCompilations.SoftwareCompilationsRequestBuilder.SoftwareCompilationsRequestBuilderGetQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<global::Marechai.ApiClient.SoftwareCompilations.SoftwareCompilationsRequestBuilder.SoftwareCompilationsRequestBuilderGetQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
return requestInfo;
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="body">The request body</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToPostRequestInformation(global::Marechai.ApiClient.Models.SoftwareCompilationDto body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToPostRequestInformation(global::Marechai.ApiClient.Models.SoftwareCompilationDto body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
if(ReferenceEquals(body, null)) throw new ArgumentNullException(nameof(body));
var requestInfo = new RequestInformation(Method.POST, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
requestInfo.SetContentFromParsable(RequestAdapter, "application/json", body);
return requestInfo;
}
/// <summary>
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
/// </summary>
/// <returns>A <see cref="global::Marechai.ApiClient.SoftwareCompilations.SoftwareCompilationsRequestBuilder"/></returns>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
public global::Marechai.ApiClient.SoftwareCompilations.SoftwareCompilationsRequestBuilder WithUrl(string rawUrl)
{
return new global::Marechai.ApiClient.SoftwareCompilations.SoftwareCompilationsRequestBuilder(rawUrl, RequestAdapter);
}
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
#pragma warning disable CS1591
public partial class SoftwareCompilationsRequestBuilderGetQueryParameters
#pragma warning restore CS1591
{
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
[QueryParameter("search")]
public string? Search { get; set; }
#nullable restore
#else
[QueryParameter("search")]
public string Search { get; set; }
#endif
[QueryParameter("skip")]
public int? Skip { get; set; }
[QueryParameter("take")]
public int? Take { get; set; }
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class SoftwareCompilationsRequestBuilderGetRequestConfiguration : RequestConfiguration<global::Marechai.ApiClient.SoftwareCompilations.SoftwareCompilationsRequestBuilder.SoftwareCompilationsRequestBuilderGetQueryParameters>
{
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class SoftwareCompilationsRequestBuilderPostRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
}
}
#pragma warning restore CS0618

View File

@@ -1,5 +1,5 @@
{
"descriptionHash": "B073097511A9F1609FF417BA45DD7A9A405EF7A5C8F1CEFD47572B6FDB30C675C9706104B1C0ABC22DA1A6645EA7B1BDC8A3394B7A7A31AC53AE9476420DDACF",
"descriptionHash": "3D337767FA6373F382CEEACB98C9C3E789A760131EC1A4FE918AF9F2725263B2A9EEC893480759A7E4CA0D820AD5ECE7CE4D55A983E77ADF51B62E457D423397",
"descriptionLocation": "../openapi.yaml",
"lockFileVersion": "1.0.0",
"kiotaVersion": "1.31.1",

View File

@@ -94,21 +94,11 @@ public partial class AdminSoftwareReleasesViewModel : ObservableObject, IRegionA
[ObservableProperty] private string _soundSynthSearchText = string.Empty;
[ObservableProperty] private ObservableCollection<SoundSynthDto> _soundSynthSuggestions = [];
// Compilation support
// Compilation support: a release may belong to a SoftwareCompilation, but
// compilation membership (included software/versions) is managed on the
// compilation itself, not on individual releases.
[ObservableProperty] private string _title = string.Empty;
[ObservableProperty] private bool _isCompilation;
[ObservableProperty] private ObservableCollection<SoftwareVersionBySoftwareReleaseDto> _includedVersions = [];
[ObservableProperty] private ObservableCollection<string> _includedVersionDisplays = [];
[ObservableProperty] private SoftwareVersionDto? _selectedIncludedVersion;
[ObservableProperty] private string _includedVersionSearchText = string.Empty;
[ObservableProperty] private ObservableCollection<SoftwareVersionDto> _includedVersionSuggestions = [];
// Versionless compilation support
[ObservableProperty] private ObservableCollection<SoftwareBySoftwareReleaseDto> _includedSoftware = [];
[ObservableProperty] private ObservableCollection<string> _includedSoftwareDisplays = [];
[ObservableProperty] private SoftwareDto? _selectedIncludedSoftware;
[ObservableProperty] private string _includedSoftwareSearchText = string.Empty;
[ObservableProperty] private ObservableCollection<SoftwareDto> _includedSoftwareSuggestions = [];
// Software picker for single releases
[ObservableProperty] private SoftwareDto? _selectedSoftware;
@@ -117,6 +107,7 @@ public partial class AdminSoftwareReleasesViewModel : ObservableObject, IRegionA
private List<SoftwareDto>? _allSoftware;
private int? _editingId;
private int? _editingCompilationId;
private List<SoftwareReleaseDto>? _allReleases;
private List<SoftwareVersionDto>? _allVersions;
private List<SoftwarePlatformDto>? _allPlatforms;
@@ -161,8 +152,6 @@ public partial class AdminSoftwareReleasesViewModel : ObservableObject, IRegionA
RemoveRecommendedGpuCommand = new AsyncRelayCommand<string>(RemoveRecommendedGpuByDisplayAsync);
AddSoundSynthCommand = new AsyncRelayCommand(AddSoundSynthAsync);
RemoveSoundSynthCommand = new AsyncRelayCommand<string>(RemoveSoundSynthByDisplayAsync);
AddIncludedVersionCommand = new AsyncRelayCommand(AddIncludedVersionAsync);
RemoveIncludedVersionCommand = new AsyncRelayCommand<string>(RemoveIncludedVersionByDisplayAsync);
AddRegionCommand = new AsyncRelayCommand(AddRegionAsync);
RemoveRegionCommand = new AsyncRelayCommand<string>(RemoveRegionByDisplayAsync);
AddLanguageCommand = new AsyncRelayCommand(AddLanguageAsync);
@@ -188,8 +177,6 @@ public partial class AdminSoftwareReleasesViewModel : ObservableObject, IRegionA
public IAsyncRelayCommand<string> RemoveRecommendedGpuCommand { get; }
public IAsyncRelayCommand AddSoundSynthCommand { get; }
public IAsyncRelayCommand<string> RemoveSoundSynthCommand { get; }
public IAsyncRelayCommand AddIncludedVersionCommand { get; }
public IAsyncRelayCommand<string> RemoveIncludedVersionCommand { get; }
public IAsyncRelayCommand AddRegionCommand { get; }
public IAsyncRelayCommand<string> RemoveRegionCommand { get; }
public IAsyncRelayCommand AddLanguageCommand { get; }
@@ -311,7 +298,8 @@ public partial class AdminSoftwareReleasesViewModel : ObservableObject, IRegionA
ReleaseDate = item.ReleaseDate;
ReleaseDatePrecision = item.ReleaseDatePrecision ?? 0;
Title = item.Title ?? string.Empty;
IsCompilation = item.IsCompilation == true;
IsCompilation = item.SoftwareCompilationId is not null;
_editingCompilationId = item.SoftwareCompilationId;
// Version picker
if(item.SoftwareVersionId.HasValue && _allVersions != null)
@@ -373,12 +361,6 @@ public partial class AdminSoftwareReleasesViewModel : ObservableObject, IRegionA
await LoadSoundSynthsAsync(item.Id.Value);
await LoadReleaseRegionsAsync(item.Id.Value);
await LoadReleaseLanguagesAsync(item.Id.Value);
if(IsCompilation)
{
await LoadIncludedVersionsAsync(item.Id.Value);
await LoadIncludedSoftwareAsync(item.Id.Value);
}
}
}
@@ -406,7 +388,7 @@ public partial class AdminSoftwareReleasesViewModel : ObservableObject, IRegionA
var dto = new SoftwareReleaseDto
{
Title = string.IsNullOrWhiteSpace(Title) ? null : Title,
IsCompilation = IsCompilation,
SoftwareCompilationId = IsCompilation ? _editingCompilationId : null,
SoftwareId = IsCompilation ? null : SelectedSoftware?.Id,
SoftwareVersionId = IsCompilation ? null : SelectedVersion?.Id,
PlatformId = SelectedPlatform?.Id,
@@ -994,180 +976,12 @@ public partial class AdminSoftwareReleasesViewModel : ObservableObject, IRegionA
SoundSynthSearchText = string.Empty;
Title = string.Empty;
IsCompilation = false;
IncludedVersions.Clear();
IncludedVersionDisplays.Clear();
SelectedIncludedVersion = null;
IncludedVersionSearchText = string.Empty;
IncludedSoftware.Clear();
IncludedSoftwareDisplays.Clear();
SelectedIncludedSoftware = null;
IncludedSoftwareSearchText = string.Empty;
_editingCompilationId = null;
SelectedSoftware = null;
SoftwareSearchText = string.Empty;
HasError = false; ErrorMessage = string.Empty;
}
// ── Included Version management (compilations only) ──
public void UpdateIncludedVersionSuggestions(string query)
{
IncludedVersionSuggestions.Clear();
if(_allVersions == null) return;
IEnumerable<SoftwareVersionDto> source = _allVersions;
if(!string.IsNullOrWhiteSpace(query))
source = source.Where(v =>
(v.VersionString != null && v.VersionString.Contains(query, StringComparison.OrdinalIgnoreCase)) ||
(v.Software != null && v.Software.Contains(query, StringComparison.OrdinalIgnoreCase)));
foreach(SoftwareVersionDto match in source) IncludedVersionSuggestions.Add(match);
}
private async Task LoadIncludedVersionsAsync(int releaseId)
{
IncludedVersions.Clear();
IncludedVersionDisplays.Clear();
try
{
List<SoftwareVersionBySoftwareReleaseDto> items = await _service.GetIncludedVersionsAsync(releaseId);
foreach(SoftwareVersionBySoftwareReleaseDto item in items)
{
IncludedVersions.Add(item);
IncludedVersionDisplays.Add($"{item.SoftwareName} — {item.SoftwareVersion}");
}
}
catch(Exception ex)
{
_logger.LogError(ex, "Error loading included versions for release {ReleaseId}", releaseId);
}
}
private async Task AddIncludedVersionAsync()
{
if(SelectedIncludedVersion?.Id == null || _editingId == null) return;
try
{
var dto = new SoftwareVersionBySoftwareReleaseDto
{
ReleaseId = _editingId.Value,
SoftwareVersionId = SelectedIncludedVersion.Id
};
await _service.AddIncludedVersionAsync(dto);
await LoadIncludedVersionsAsync(_editingId.Value);
SelectedIncludedVersion = null;
IncludedVersionSearchText = string.Empty;
}
catch(Exception ex)
{
_logger.LogError(ex, "Error adding included version");
ErrorMessage = _localizer["FailedToAddIncludedVersion"];
HasError = true;
}
}
private async Task RemoveIncludedVersionByDisplayAsync(string? display)
{
if(display == null || _editingId == null) return;
int idx = IncludedVersionDisplays.IndexOf(display);
if(idx < 0 || idx >= IncludedVersions.Count) return;
SoftwareVersionBySoftwareReleaseDto iv = IncludedVersions[idx];
try
{
await _service.RemoveIncludedVersionAsync(_editingId.Value, (int)(iv.SoftwareVersionId ?? 0));
await LoadIncludedVersionsAsync(_editingId.Value);
}
catch(Exception ex)
{
_logger.LogError(ex, "Error removing included version");
ErrorMessage = _localizer["FailedToRemoveIncludedVersion"];
HasError = true;
}
}
// --- Versionless compilation included software ---
private async Task LoadIncludedSoftwareAsync(int releaseId)
{
IncludedSoftware.Clear();
IncludedSoftwareDisplays.Clear();
try
{
List<SoftwareBySoftwareReleaseDto> items = await _service.GetIncludedSoftwareAsync(releaseId);
foreach(SoftwareBySoftwareReleaseDto item in items)
{
IncludedSoftware.Add(item);
IncludedSoftwareDisplays.Add(item.SoftwareName ?? string.Empty);
}
}
catch(Exception ex)
{
_logger.LogError(ex, "Error loading included software for release {ReleaseId}", releaseId);
}
}
private async Task AddIncludedSoftwareAsync()
{
if(SelectedIncludedSoftware?.Id == null || _editingId == null) return;
try
{
var dto = new SoftwareBySoftwareReleaseDto
{
ReleaseId = _editingId.Value,
SoftwareId = SelectedIncludedSoftware.Id
};
await _service.AddIncludedSoftwareAsync(dto);
await LoadIncludedSoftwareAsync(_editingId.Value);
SelectedIncludedSoftware = null;
IncludedSoftwareSearchText = string.Empty;
}
catch(Exception ex)
{
_logger.LogError(ex, "Error adding included software");
ErrorMessage = _localizer["FailedToAddIncludedSoftware"];
HasError = true;
}
}
private async Task RemoveIncludedSoftwareByDisplayAsync(string? display)
{
if(display == null || _editingId == null) return;
int idx = IncludedSoftwareDisplays.IndexOf(display);
if(idx < 0 || idx >= IncludedSoftware.Count) return;
SoftwareBySoftwareReleaseDto sw = IncludedSoftware[idx];
try
{
await _service.RemoveIncludedSoftwareAsync(_editingId.Value, (int)(sw.SoftwareId ?? 0));
await LoadIncludedSoftwareAsync(_editingId.Value);
}
catch(Exception ex)
{
_logger.LogError(ex, "Error removing included software");
ErrorMessage = _localizer["FailedToRemoveIncludedSoftware"];
HasError = true;
}
}
private void UpdateIncludedSoftwareSuggestions(string query)
{
IncludedSoftwareSuggestions.Clear();
if(_allSoftware == null) return;
IEnumerable<SoftwareDto> source = _allSoftware;
if(!string.IsNullOrWhiteSpace(query))
source = source.Where(s =>
s.Name != null && s.Name.Contains(query, StringComparison.OrdinalIgnoreCase));
foreach(SoftwareDto match in source) IncludedSoftwareSuggestions.Add(match);
}
private void UpdateSoftwareSuggestions(string query)
{
SoftwareSuggestions.Clear();

View File

@@ -92,9 +92,6 @@ public partial class SoftwareReleaseViewViewModel : ObservableObject, IRegionAwa
[ObservableProperty]
private Visibility _showCompanies = Visibility.Collapsed;
[ObservableProperty]
private Visibility _showIncludedVersions = Visibility.Collapsed;
[ObservableProperty]
private bool _isCompilation;
@@ -112,7 +109,6 @@ public partial class SoftwareReleaseViewViewModel : ObservableObject, IRegionAwa
public ObservableCollection<string> Barcodes { get; } = [];
public ObservableCollection<string> ProductCodes { get; } = [];
public ObservableCollection<string> Companies { get; } = [];
public ObservableCollection<string> IncludedVersions { get; } = [];
public bool IsNavigationTarget(NavigationContext navigationContext) => false;
@@ -161,7 +157,6 @@ public partial class SoftwareReleaseViewViewModel : ObservableObject, IRegionAwa
Barcodes.Clear();
ProductCodes.Clear();
Companies.Clear();
IncludedVersions.Clear();
SoftwareReleaseDto? release = await _browsingService.GetReleaseByIdAsync(releaseId);
@@ -188,26 +183,13 @@ public partial class SoftwareReleaseViewViewModel : ObservableObject, IRegionAwa
ReleaseDateDisplay = (release.ReleaseDatePrecision ?? 0) == 2 ? $"{release.ReleaseDate.Value.Year}" : (release.ReleaseDatePrecision ?? 0) == 1 ? release.ReleaseDate.Value.ToString("MMMM yyyy") : release.ReleaseDate.Value.DateTime.ToString("MMMM d, yyyy");
// Determine if this is a compilation
IsCompilation = release.IsCompilation == true;
IsCompilation = release.SoftwareCompilationId is not null;
if(IsCompilation)
{
// Compilation: use Title or fallback
// Compilation: use Title or fallback. Included software/versions are
// managed and displayed on the compilation's own page, not here.
ReleaseTitle = release.Title ?? _localizer["Compilation"];
// Load included versions (versioned compilations)
List<SoftwareVersionBySoftwareReleaseDto> includedVersions =
await _browsingService.GetIncludedVersionsAsync(releaseId);
foreach(SoftwareVersionBySoftwareReleaseDto iv in includedVersions)
IncludedVersions.Add($"{iv.SoftwareName} — {iv.SoftwareVersion}");
// Load included software (versionless compilations)
List<SoftwareBySoftwareReleaseDto> includedSoftware =
await _browsingService.GetIncludedSoftwareAsync(releaseId);
foreach(SoftwareBySoftwareReleaseDto sw in includedSoftware)
IncludedVersions.Add(sw.SoftwareName ?? string.Empty);
}
else
{
@@ -277,7 +259,6 @@ public partial class SoftwareReleaseViewViewModel : ObservableObject, IRegionAwa
ShowBarcodes = Barcodes.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
ShowProductCodes = ProductCodes.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
ShowCompanies = Companies.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
ShowIncludedVersions = IncludedVersions.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
}
private async Task LoadAllCompaniesAsync(SoftwareReleaseDto release)

View File

@@ -76,9 +76,6 @@
<TextBox Header="Title"
Text="{Binding Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
PlaceholderText="Optional. Used for compilation releases." />
<CheckBox Content="Is Compilation"
IsChecked="{Binding IsCompilation, Mode=TwoWay}"
IsEnabled="{Binding IsEditingExisting, Converter={StaticResource BoolNegationConverter}}" />
<AutoSuggestBox Header="{Binding Source={StaticResource Strings}, Path=SoftwareVersionLabel}"
PlaceholderText="{Binding Source={StaticResource Strings}, Path=SearchPlaceholder}"
Text="{Binding VersionSearchText, Mode=TwoWay}" QueryIcon="Find"
@@ -303,41 +300,6 @@
<Button Content="+" Command="{Binding AddSoundSynthCommand}" Style="{ThemeResource AccentButtonStyle}" Padding="8,4" />
</StackPanel>
<!-- Included Software Versions Section (compilation only, edit only) -->
<StackPanel Visibility="{Binding IsCompilation, Converter={StaticResource BoolToVisibilityConverter}}"
Spacing="8" Margin="0,16,0,0">
<TextBlock Text="Included Software Versions" FontSize="16" FontWeight="SemiBold" />
<ItemsRepeater ItemsSource="{Binding IncludedVersionDisplays}">
<ItemsRepeater.Layout><StackLayout Spacing="4" /></ItemsRepeater.Layout>
<ItemsRepeater.ItemTemplate>
<DataTemplate>
<Grid ColumnSpacing="4">
<Grid.ColumnDefinitions><ColumnDefinition Width="*" /><ColumnDefinition Width="Auto" /></Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding}" VerticalAlignment="Center" TextWrapping="Wrap" />
<Button Grid.Column="1" Content="&#xE74D;" FontFamily="Segoe MDL2 Assets"
Command="{Binding DataContext.RemoveIncludedVersionCommand, ElementName=PageRoot}"
CommandParameter="{Binding}" Padding="4,2" Style="{ThemeResource TextBlockButtonStyle}" />
</Grid>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
<AutoSuggestBox Header="Search Software Version"
PlaceholderText="{Binding Source={StaticResource Strings}, Path=SearchPlaceholder}"
Text="{Binding IncludedVersionSearchText, Mode=TwoWay}" QueryIcon="Find"
x:Name="IncludedVersionSearchBox" TextChanged="IncludedVersionSearchBox_TextChanged" />
<ComboBox ItemsSource="{Binding IncludedVersionSuggestions}" SelectedItem="{Binding SelectedIncludedVersion, Mode=TwoWay}"
HorizontalAlignment="Stretch" PlaceholderText="Select version">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding Software}" /><Run Text=" — " /><Run Text="{Binding VersionString}" />
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Content="+" Command="{Binding AddIncludedVersionCommand}" Style="{ThemeResource AccentButtonStyle}" Padding="8,4" />
</StackPanel>
</StackPanel>
</ScrollViewer>
<Grid Grid.Row="2" Padding="16,12" BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}" BorderThickness="0,1,0,0" ColumnSpacing="8">

View File

@@ -65,9 +65,4 @@ public sealed partial class AdminSoftwareReleasesPage : Page
vm.UpdateSoundSynthSuggestions(sender.Text);
}
private void IncludedVersionSearchBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
if(args.Reason == AutoSuggestionBoxTextChangeReason.UserInput && DataContext is AdminSoftwareReleasesViewModel vm)
vm.UpdateIncludedVersionSuggestions(sender.Text);
}
}

View File

@@ -232,31 +232,6 @@
</StackPanel>
</Grid>
<!-- Included Software (compilations only) -->
<StackPanel Visibility="{Binding ShowIncludedVersions}"
Spacing="12">
<TextBlock
Text="Included Software"
FontSize="14"
FontWeight="SemiBold"
Foreground="{ThemeResource TextControlForeground}" />
<ItemsControl ItemsSource="{Binding IncludedVersions}"
Margin="0,0,0,0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
CornerRadius="8"
Padding="12,8"
Margin="0,0,0,8">
<TextBlock Text="{Binding}"
FontSize="14"
Foreground="{ThemeResource TextControlForeground}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<!-- Barcodes -->
<StackPanel Visibility="{Binding ShowBarcodes}"
Spacing="12">

View File

@@ -520,23 +520,6 @@ public class SoftwareBrowsingService
}
}
public async Task<List<SoftwareVersionBySoftwareReleaseDto>> GetIncludedVersionsAsync(int releaseId)
{
try
{
List<SoftwareVersionBySoftwareReleaseDto> versions =
await _apiClient.Software.Releases[releaseId].Versions.GetAsync();
return versions ?? [];
}
catch(Exception ex)
{
_logger.LogError(ex, "Error fetching included versions for release {ReleaseId}", releaseId);
return [];
}
}
public async Task<List<SoftwareReleaseDto>> GetCompilationsForSoftwareAsync(int softwareId)
{
try
@@ -571,23 +554,6 @@ public class SoftwareBrowsingService
}
}
public async Task<List<SoftwareBySoftwareReleaseDto>> GetIncludedSoftwareAsync(int releaseId)
{
try
{
List<SoftwareBySoftwareReleaseDto> software =
await _apiClient.Software.Releases[releaseId].Software.GetAsync();
return software ?? [];
}
catch(Exception ex)
{
_logger.LogError(ex, "Error fetching included software for release {ReleaseId}", releaseId);
return [];
}
}
public async Task<SoftwareDescriptionDto> GetDescriptionAsync(int softwareId, string languageCode)
{
try

View File

@@ -364,63 +364,6 @@ public class SoftwareReleasesService
}
}
// ── Compilation junction methods ──
public async Task<List<SoftwareVersionBySoftwareReleaseDto>> GetIncludedVersionsAsync(int releaseId)
{
try
{
List<SoftwareVersionBySoftwareReleaseDto>? versions =
await _apiClient.Software.Releases[releaseId].Versions.GetAsync();
return versions ?? [];
}
catch(Exception ex)
{
_logger.LogError(ex, "Error loading included versions for release {ReleaseId}", releaseId);
return [];
}
}
public async Task AddIncludedVersionAsync(SoftwareVersionBySoftwareReleaseDto dto)
{
await _apiClient.Software.Releases[(int)(dto.ReleaseId ?? 0)].Versions.PostAsync(dto);
}
public async Task RemoveIncludedVersionAsync(int releaseId, int versionId)
{
await _apiClient.Software.Releases[releaseId].Versions[versionId].DeleteAsync();
}
// --- Versionless Compilation Junction ---
public async Task<List<SoftwareBySoftwareReleaseDto>> GetIncludedSoftwareAsync(int releaseId)
{
try
{
List<SoftwareBySoftwareReleaseDto>? software =
await _apiClient.Software.Releases[releaseId].Software.GetAsync();
return software ?? [];
}
catch(Exception ex)
{
_logger.LogError(ex, "Error loading included software for release {ReleaseId}", releaseId);
return [];
}
}
public async Task AddIncludedSoftwareAsync(SoftwareBySoftwareReleaseDto dto)
{
await _apiClient.Software.Releases[(int)(dto.ReleaseId ?? 0)].Software.PostAsync(dto);
}
public async Task RemoveIncludedSoftwareAsync(int releaseId, int softwareId)
{
await _apiClient.Software.Releases[releaseId].Software[softwareId].DeleteAsync();
}
// --- By Software ---

View File

@@ -183,11 +183,13 @@ public class CompilationRelationService
var containedSoftwareIds = new List<ulong>();
var unresolvedSlugs = new List<string>();
var containedCompilationIds = new List<ulong>();
foreach(string gameSlug in gameSlugs)
{
ulong? softwareId = await ResolveGameSlugAsync(context, gameSlug, dryRun);
SlugResolution resolution = await ResolveGameSlugAsync(context, gameSlug, dryRun);
if(softwareId is null)
if(!resolution.IsResolved)
{
Console.WriteLine($" WARNING: Cannot resolve '{gameSlug}'.");
unresolvedSlugs.Add(gameSlug);
@@ -195,14 +197,22 @@ public class CompilationRelationService
continue;
}
containedSoftwareIds.Add(softwareId.Value);
Console.WriteLine($" Resolved '{gameSlug}' → Software ID: {softwareId}");
if(resolution.SoftwareCompilationId is not null)
{
containedCompilationIds.Add(resolution.SoftwareCompilationId.Value);
Console.WriteLine($" Resolved '{gameSlug}' → SoftwareCompilation ID: {resolution.SoftwareCompilationId}");
}
else
{
containedSoftwareIds.Add(resolution.SoftwareId!.Value);
Console.WriteLine($" Resolved '{gameSlug}' → Software ID: {resolution.SoftwareId}");
}
}
bool hasUnresolved = unresolvedSlugs.Count > 0 || unresolvable.Count > 0;
bool compilationCreated = false;
if(containedSoftwareIds.Count == 0)
if(containedSoftwareIds.Count == 0 && containedCompilationIds.Count == 0)
{
Console.WriteLine(" FAILED: No contained games could be resolved; compilation not created.");
@@ -223,7 +233,8 @@ public class CompilationRelationService
// Conversion phase
if(dryRun)
{
Console.WriteLine($" Would convert to compilation with {containedSoftwareIds.Count} game(s)" +
Console.WriteLine($" Would convert to compilation with {containedSoftwareIds.Count} game(s) " +
$"and {containedCompilationIds.Count} sub-compilation(s)" +
(hasUnresolved
? $" and send admin report ({unresolvedSlugs.Count} unresolved slug(s), " +
$"{unresolvable.Count} unresolvable anchor(s))"
@@ -250,37 +261,67 @@ public class CompilationRelationService
continue;
}
// Convert each release to a compilation release
// Create the independent SoftwareCompilation entity backing these releases.
// Save immediately so its Id is assigned before it's referenced below.
var newCompilation = new SoftwareCompilation { Name = compilation.Name };
context.SoftwareCompilations.Add(newCompilation);
await context.SaveChangesAsync();
ulong newCompilationId = newCompilation.Id;
// Re-point each release at the new compilation instead of flagging it.
foreach(var release in releases)
{
release.IsCompilation = true;
release.Title = compilation.Name;
release.SoftwareId = null;
release.SoftwareCompilationId = newCompilationId;
release.Title = compilation.Name;
release.SoftwareId = null;
}
// Add junction entries for contained games
foreach(ulong containedId in containedSoftwareIds)
// Contained software/versions belong to the compilation itself (shared across
// all of its releases), not to any one release.
foreach(ulong containedId in containedSoftwareIds)
{
bool junctionExists = await context.SoftwareBySoftwareCompilation
.AnyAsync(j => j.SoftwareCompilationId == newCompilationId && j.SoftwareId == containedId);
if(!junctionExists)
{
bool junctionExists = await context.SoftwareBySoftwareRelease
.AnyAsync(j => j.ReleaseId == release.Id && j.SoftwareId == containedId);
if(!junctionExists)
context.SoftwareBySoftwareCompilation.Add(new SoftwareBySoftwareCompilation
{
context.SoftwareBySoftwareRelease.Add(new SoftwareBySoftwareRelease
{
ReleaseId = release.Id,
SoftwareId = containedId
});
}
SoftwareCompilationId = newCompilationId,
SoftwareId = containedId
});
}
}
// Update import state: clear SoftwareId (compilation has no Software).
// Contained sub-compilations: a slug previously converted into its own
// SoftwareCompilation is linked as a nested member rather than dropped.
foreach(ulong containedCompilationId in containedCompilationIds)
{
bool junctionExists = await context.SoftwareCompilationBySoftwareCompilation
.AnyAsync(j => j.ParentCompilationId == newCompilationId &&
j.ChildCompilationId == containedCompilationId);
if(!junctionExists)
{
context.SoftwareCompilationBySoftwareCompilation.Add(new SoftwareCompilationBySoftwareCompilation
{
ParentCompilationId = newCompilationId,
ChildCompilationId = containedCompilationId
});
}
}
// Update import state: record the new compilation pointer instead of just
// nulling SoftwareId. This is the fix for the long-standing bug where a slug
// converted into a compilation had no replacement pointer, so re-encountering
// it as a sub-item of another compilation could never resolve.
// LEGITIMATE null-out: this is one of only two places allowed to clear
// MobyGamesImportState.SoftwareId. It is paired with the Softwares.Remove(compilation)
// call below, so the slug<->Software invariant holds (the Software is going away).
// See MarkFailedAsync / MarkRejectedAsync in StateService.cs which intentionally
// preserve SoftwareId on every other path.
importState.SoftwareId = null;
importState.SoftwareId = null;
importState.SoftwareCompilationId = newCompilationId;
// Save before deleting the Software (SoftwareRelease FK is Restrict, but we already nulled it)
await context.SaveChangesAsync();
@@ -296,24 +337,24 @@ public class CompilationRelationService
context.SoftwareCompanyRoles.RemoveRange(companyRoles);
// SoftwareBySoftwareRelease.SoftwareId \u2192 Softwares.Id is also Restrict (no
// SoftwareBySoftwareCompilation.SoftwareId \u2192 Softwares.Id is also Restrict (no
// cascade). When THIS Software is itself listed as a contained game in some
// OTHER compilation's release (parent compilation), those junction rows
// block the delete. Drop them with a warning so the operator knows the
// parent compilation lost a member and can re-resolve it later.
var inboundJunctions = await context.SoftwareBySoftwareRelease
// OTHER compilation (parent compilation), those junction rows block the delete.
// Drop them with a warning so the operator knows the parent compilation lost a
// member and can re-resolve it later.
var inboundJunctions = await context.SoftwareBySoftwareCompilation
.Where(j => j.SoftwareId == compilation.Id)
.ToListAsync();
if(inboundJunctions.Count > 0)
{
var parentReleaseIds = inboundJunctions.Select(j => j.ReleaseId).Distinct().ToList();
var parentCompilationIds = inboundJunctions.Select(j => j.SoftwareCompilationId).Distinct().ToList();
Console.WriteLine($" \e[33mWarning: {inboundJunctions.Count} parent-compilation junction(s) " +
$"referenced this Software (parent release IDs: " +
$"{string.Join(", ", parentReleaseIds)}); dropping them so the " +
$"referenced this Software (parent compilation IDs: " +
$"{string.Join(", ", parentCompilationIds)}); dropping them so the " +
$"Software delete can proceed. Parent compilation(s) will lose a member " +
$"until re-resolved.\e[0m");
context.SoftwareBySoftwareRelease.RemoveRange(inboundJunctions);
context.SoftwareBySoftwareCompilation.RemoveRange(inboundJunctions);
}
context.Softwares.Remove(compilation);
@@ -323,8 +364,9 @@ public class CompilationRelationService
if(hasUnresolved)
{
Console.WriteLine($" Converted {releases.Count} release(s) to PARTIAL compilation, " +
$"linked {containedSoftwareIds.Count} game(s), " +
Console.WriteLine($" Converted {releases.Count} release(s) to PARTIAL compilation " +
$"(ID {newCompilationId}), linked {containedSoftwareIds.Count} game(s) " +
$"and {containedCompilationIds.Count} sub-compilation(s), " +
$"{unresolvedSlugs.Count} unresolved slug(s), " +
$"{unresolvable.Count} unresolvable anchor(s), " +
$"deleted orphaned Software ID {compilation.Id}");
@@ -333,8 +375,9 @@ public class CompilationRelationService
}
else
{
Console.WriteLine($" Converted {releases.Count} release(s) to compilation, " +
$"linked {containedSoftwareIds.Count} game(s), " +
Console.WriteLine($" Converted {releases.Count} release(s) to compilation " +
$"(ID {newCompilationId}), linked {containedSoftwareIds.Count} game(s) " +
$"and {containedCompilationIds.Count} sub-compilation(s), " +
$"deleted orphaned Software ID {compilation.Id}");
converted++;
@@ -377,13 +420,29 @@ public class CompilationRelationService
}
/// <summary>
/// Resolves a MobyGames game slug to a Software ID using local database only.
/// Tries all slug variants (with/without leading dash) in MobyGamesImportState,
/// then attempts to import from mobygames_raw if not found.
/// The result of resolving a MobyGames game slug to a local entity. At most one of
/// <see cref="SoftwareId"/> / <see cref="SoftwareCompilationId"/> is set; both null means
/// unresolved.
/// </summary>
async Task<ulong?> ResolveGameSlugAsync(MarechaiContext context, string slug, bool dryRun)
readonly record struct SlugResolution(ulong? SoftwareId, ulong? SoftwareCompilationId)
{
if(string.IsNullOrWhiteSpace(slug)) return null;
public bool IsResolved => SoftwareId is not null || SoftwareCompilationId is not null;
public static readonly SlugResolution Unresolved = new(null, null);
}
/// <summary>
/// Resolves a MobyGames game slug to a local Software or SoftwareCompilation using the
/// local database only. Tries all slug variants (with/without leading dash) in
/// MobyGamesImportState, then attempts to import from mobygames_raw if not found.
/// A slug that was previously converted into a compilation has its
/// MobyGamesImportState.SoftwareCompilationId pointer set instead of SoftwareId — this is
/// checked first so re-encountering that slug as a sub-item of another compilation
/// resolves to the existing compilation instead of failing.
/// </summary>
async Task<SlugResolution> ResolveGameSlugAsync(MarechaiContext context, string slug, bool dryRun)
{
if(string.IsNullOrWhiteSpace(slug)) return SlugResolution.Unresolved;
string trimmed = slug.TrimStart('-');
@@ -396,10 +455,12 @@ public class CompilationRelationService
.FirstOrDefaultAsync(s => s.MobyGameId == trySlug &&
s.Status == MobyGamesImportStatus.Imported);
if(state?.SoftwareId is not null) return state.SoftwareId;
if(state?.SoftwareCompilationId is not null) return new SlugResolution(null, state.SoftwareCompilationId);
if(state?.SoftwareId is not null) return new SlugResolution(state.SoftwareId, null);
}
// Try importing from mobygames_raw
// Try importing from mobygames_raw. Fresh imports are never pre-converted, so this
// always yields a Software (never a SoftwareCompilation).
foreach(string trySlug in slugsToTry.Distinct())
{
var rows = await _sourceDb.GetRowsForGameAsync(trySlug);
@@ -410,7 +471,7 @@ public class CompilationRelationService
{
Console.Write($" [dry-run: would import '{trySlug}']");
return 0; // placeholder for dry-run
return new SlugResolution(0, null); // placeholder for dry-run
}
Console.Write($" Importing '{trySlug}' from mobygames_raw...");
@@ -420,13 +481,13 @@ public class CompilationRelationService
{
Console.WriteLine($" OK (ID: {importedId})");
return importedId;
return new SlugResolution(importedId, null);
}
Console.WriteLine(" failed");
}
}
return null;
return SlugResolution.Unresolved;
}
}

View File

@@ -1116,33 +1116,39 @@ public class ImportService
try
{
// Creation phase: create compilation releases (no Software record)
// Creation phase: create the independent SoftwareCompilation entity, then its
// releases (no Software record on the releases themselves).
var newCompilation = new SoftwareCompilation { Name = game.Name };
context.SoftwareCompilations.Add(newCompilation);
await context.SaveChangesAsync();
ulong newCompilationId = newCompilation.Id;
List<SoftwareRelease> createdReleases;
if(game.Releases.Count > 0)
createdReleases = await ImportCompilationReleasesAsync(context, game, game.Name);
createdReleases = await ImportCompilationReleasesAsync(context, game, newCompilationId, game.Name);
else
createdReleases = await ImportBasicCompilationReleaseAsync(context, game, game.Name);
createdReleases = await ImportBasicCompilationReleaseAsync(context, game, newCompilationId, game.Name);
if(createdReleases.Count == 0)
{
Console.WriteLine(" WARNING: No releases created for compilation.");
context.SoftwareCompilations.Remove(newCompilation);
await context.SaveChangesAsync();
await _stateService.MarkFailedAsync(game.MobyGameId, "No releases created", batchNumber);
return;
}
// Add SoftwareBySoftwareRelease junction entries
foreach(var release in createdReleases)
// Contained software belongs to the compilation itself (shared across all of its
// releases), not to any one release.
foreach(ulong containedId in containedSoftwareIds)
{
foreach(ulong containedId in containedSoftwareIds)
context.SoftwareBySoftwareCompilation.Add(new SoftwareBySoftwareCompilation
{
context.SoftwareBySoftwareRelease.Add(new SoftwareBySoftwareRelease
{
ReleaseId = release.Id,
SoftwareId = containedId
});
}
SoftwareCompilationId = newCompilationId,
SoftwareId = containedId
});
}
await context.SaveChangesAsync();
@@ -1165,24 +1171,24 @@ public class ImportService
foreach(var release in existingReleases)
{
release.IsCompilation = true;
release.Title = game.Name;
release.SoftwareId = null;
release.SoftwareCompilationId = newCompilationId;
release.Title = game.Name;
release.SoftwareId = null;
}
// Add junction entries for contained games
foreach(ulong containedId in containedSoftwareIds)
// Add junction entries for contained games (compilation-level, not per-release)
foreach(ulong containedId in containedSoftwareIds)
{
bool junctionExists = await context.SoftwareBySoftwareCompilation
.AnyAsync(j => j.SoftwareCompilationId == newCompilationId && j.SoftwareId == containedId);
if(!junctionExists)
{
bool junctionExists = await context.SoftwareBySoftwareRelease
.AnyAsync(j => j.ReleaseId == release.Id && j.SoftwareId == containedId);
if(!junctionExists)
context.SoftwareBySoftwareCompilation.Add(new SoftwareBySoftwareCompilation
{
context.SoftwareBySoftwareRelease.Add(new SoftwareBySoftwareRelease
{
ReleaseId = release.Id,
SoftwareId = containedId
});
}
SoftwareCompilationId = newCompilationId,
SoftwareId = containedId
});
}
}
@@ -1213,8 +1219,10 @@ public class ImportService
Console.WriteLine($" Cleaned up orphaned Software ID: {orphan.Id}");
}
// Mark as imported with no SoftwareId (compilations don't have a Software record)
await _stateService.MarkImportedAsync(game.MobyGameId, batchNumber, null);
// Mark as imported with no SoftwareId compilations are tracked via SoftwareCompilationId
// instead, so that re-encountering this slug as a sub-item of another compilation resolves.
await _stateService.MarkImportedAsync(game.MobyGameId, batchNumber, null,
softwareCompilationId: newCompilationId);
compilationCreated = true;
@@ -1299,21 +1307,21 @@ public class ImportService
HashSet<(ulong, int, string)> addedCompanyRoles)
{
await ImportReleasesInternalAsync(context, software?.Id, game, addedCompanyRoles,
false, null);
null, null);
}
async Task<List<SoftwareRelease>> ImportCompilationReleasesAsync(
MarechaiContext context, ParsedGame game, string compilationTitle)
MarechaiContext context, ParsedGame game, ulong softwareCompilationId, string compilationTitle)
{
return await ImportReleasesInternalAsync(context, null, game,
new HashSet<(ulong, int, string)>(),
true, compilationTitle);
softwareCompilationId, compilationTitle);
}
async Task<List<SoftwareRelease>> ImportReleasesInternalAsync(
MarechaiContext context, ulong? softwareId, ParsedGame game,
HashSet<(ulong, int, string)> addedCompanyRoles,
bool isCompilation, string compilationTitle)
ulong? softwareCompilationId, string compilationTitle)
{
var addedProductCodes = new HashSet<(ProductCodeIssuer, string)>();
var addedBarcodes = new HashSet<string>();
@@ -1337,13 +1345,13 @@ public class ImportService
var dbRelease = new SoftwareRelease
{
SoftwareId = isCompilation ? null : softwareId,
PlatformId = platform?.Id,
PublisherId = publisher.Id,
ReleaseDate = releaseDate,
ReleaseDatePrecision = precision,
IsCompilation = isCompilation,
Title = isCompilation ? compilationTitle : release.Comments
SoftwareId = softwareCompilationId is not null ? null : softwareId,
PlatformId = platform?.Id,
PublisherId = publisher.Id,
ReleaseDate = releaseDate,
ReleaseDatePrecision = precision,
SoftwareCompilationId = softwareCompilationId,
Title = softwareCompilationId is not null ? compilationTitle : release.Comments
};
context.SoftwareReleases.Add(dbRelease);
@@ -1586,18 +1594,18 @@ public class ImportService
async Task ImportBasicReleaseAsync(MarechaiContext context, Software software, ParsedGame game)
{
await ImportBasicReleaseInternalAsync(context, software?.Id, game, false, null);
await ImportBasicReleaseInternalAsync(context, software?.Id, game, null, null);
}
async Task<List<SoftwareRelease>> ImportBasicCompilationReleaseAsync(
MarechaiContext context, ParsedGame game, string compilationTitle)
MarechaiContext context, ParsedGame game, ulong softwareCompilationId, string compilationTitle)
{
return await ImportBasicReleaseInternalAsync(context, null, game, true, compilationTitle);
return await ImportBasicReleaseInternalAsync(context, null, game, softwareCompilationId, compilationTitle);
}
async Task<List<SoftwareRelease>> ImportBasicReleaseInternalAsync(
MarechaiContext context, ulong? softwareId, ParsedGame game,
bool isCompilation, string compilationTitle)
ulong? softwareCompilationId, string compilationTitle)
{
// Create one release per platform from Main tab data
var (publisher, _) = await _companyMatcher.MatchOrCreateAsync(game.Publishers.FirstOrDefault());
@@ -1613,13 +1621,13 @@ public class ImportService
var dbRelease = new SoftwareRelease
{
SoftwareId = isCompilation ? null : softwareId,
PlatformId = platform?.Id,
PublisherId = publisher.Id,
ReleaseDate = releaseDate,
ReleaseDatePrecision = precision,
IsCompilation = isCompilation,
Title = isCompilation ? compilationTitle : null
SoftwareId = softwareCompilationId is not null ? null : softwareId,
PlatformId = platform?.Id,
PublisherId = publisher.Id,
ReleaseDate = releaseDate,
ReleaseDatePrecision = precision,
SoftwareCompilationId = softwareCompilationId,
Title = softwareCompilationId is not null ? compilationTitle : null
};
context.SoftwareReleases.Add(dbRelease);

View File

@@ -256,17 +256,16 @@ public class OrphanCleanupService
.ExecuteUpdateAsync(s => s.SetProperty(m => m.SoftwareId, _ => twinId));
// ====================================================================
// 7. SoftwareBySoftwareRelease — composite PK (SoftwareId, ReleaseId).
// 7. SoftwareBySoftwareCompilation — composite PK (SoftwareId, SoftwareCompilationId).
// ====================================================================
// Step 1 already cascade-deleted rows pointing at deleted orphan releases.
// Remaining rows have SoftwareId=orphanId pointing at twin/third-party releases.
await context.SoftwareBySoftwareRelease
// Remaining rows have SoftwareId=orphanId pointing at twin/third-party compilations.
await context.SoftwareBySoftwareCompilation
.Where(j => j.SoftwareId == orphanId &&
context.SoftwareBySoftwareRelease.Any(t => t.SoftwareId == twinId &&
t.ReleaseId == j.ReleaseId))
context.SoftwareBySoftwareCompilation.Any(t => t.SoftwareId == twinId &&
t.SoftwareCompilationId == j.SoftwareCompilationId))
.ExecuteDeleteAsync();
await context.SoftwareBySoftwareRelease.Where(j => j.SoftwareId == orphanId)
await context.SoftwareBySoftwareCompilation.Where(j => j.SoftwareId == orphanId)
.ExecuteUpdateAsync(s => s.SetProperty(j => j.SoftwareId, _ => twinId));
// ====================================================================

View File

@@ -199,7 +199,7 @@ public class SpecsReparseService
List<SoftwareRelease> matches = await context.SoftwareReleases
.Include(r => r.Attributes)
.Where(r => r.IsCompilation && r.Title == title)
.Where(r => r.SoftwareCompilationId != null && r.Title == title)
.ToListAsync();
if(matches.Count == 0)

View File

@@ -76,7 +76,7 @@ public class StateService
}
public async Task MarkImportedAsync(string mobyGameId, int batchNumber, ulong? softwareId,
int? mobyNumericId = null)
int? mobyNumericId = null, ulong? softwareCompilationId = null)
{
await using var context = await _contextFactory.CreateDbContextAsync();
@@ -85,22 +85,24 @@ public class StateService
if(existing != null)
{
existing.Status = MobyGamesImportStatus.Imported;
existing.ProcessedOn = DateTime.UtcNow;
existing.BatchNumber = batchNumber;
existing.SoftwareId = softwareId;
existing.MobyNumericId = mobyNumericId ?? existing.MobyNumericId;
existing.Status = MobyGamesImportStatus.Imported;
existing.ProcessedOn = DateTime.UtcNow;
existing.BatchNumber = batchNumber;
existing.SoftwareId = softwareId;
existing.SoftwareCompilationId = softwareCompilationId;
existing.MobyNumericId = mobyNumericId ?? existing.MobyNumericId;
}
else
{
context.MobyGamesImportStates.Add(new MobyGamesImportState
{
MobyGameId = mobyGameId,
Status = MobyGamesImportStatus.Imported,
ProcessedOn = DateTime.UtcNow,
BatchNumber = batchNumber,
SoftwareId = softwareId,
MobyNumericId = mobyNumericId
MobyGameId = mobyGameId,
Status = MobyGamesImportStatus.Imported,
ProcessedOn = DateTime.UtcNow,
BatchNumber = batchNumber,
SoftwareId = softwareId,
SoftwareCompilationId = softwareCompilationId,
MobyNumericId = mobyNumericId
});
}

View File

@@ -0,0 +1,417 @@
@using Marechai.ApiClient.Models
@using Marechai.Services
@using System.Threading
@inject IStringLocalizer<SoftwareCompilationsService> L
<MudDialog>
<DialogContent>
<MudTabs Elevation="0" Rounded="true" ApplyEffectsToContainer="true" TabPanelsClass="pa-4">
@* Tab 1 - Basic Info *@
<MudTabPanel Text="@L["Basic Info"]" Icon="@Icons.Material.Filled.Info">
<MudTextField @bind-Value="Name"
Label="@L["Name"]"
Variant="Variant.Outlined"
Required="true"
RequiredError="@L["Name is required."]"
Class="mb-4"/>
<MudAutocomplete T="SoftwareDto"
Label="@L["Bundled with software (optional)"]"
@bind-Value="_selectedSoftware"
SearchFunc="SearchSoftware"
ToStringFunc="@(s => s?.Name ?? string.Empty)"
Variant="Variant.Outlined"
Clearable="true"
MinCharacters="0"
Class="mb-4"
HelperText="@L["Set if this compilation is permanently bundled with a piece of software (e.g. games shipped with an OS)."]"/>
<MudAutocomplete T="MachineDto"
Label="@L["Bundled with machine (optional)"]"
@bind-Value="_selectedMachine"
SearchFunc="SearchMachines"
ToStringFunc="@(m => m?.Name ?? string.Empty)"
Variant="Variant.Outlined"
Clearable="true"
MinCharacters="0"
Class="mb-4"
HelperText="@L["Set if this compilation is permanently bundled with a machine (e.g. a dedicated handheld)."]"/>
<MudAutocomplete T="SoftwareCompilationDto"
Label="@L["Predecessor"]"
@bind-Value="_selectedPredecessor"
SearchFunc="SearchPredecessors"
ToStringFunc="@(c => c?.Name ?? string.Empty)"
Variant="Variant.Outlined"
Clearable="true"
MinCharacters="0"
Class="mb-4"/>
<MudSelect T="Marechai.Data.SoftwareRelationshipType"
@bind-Value="RelationshipType"
Label="@L["Relationship to predecessor"]"
Variant="Variant.Outlined"
Disabled="@(_selectedPredecessor is null)"
Class="mb-4">
<MudSelectItem T="Marechai.Data.SoftwareRelationshipType" Value="Marechai.Data.SoftwareRelationshipType.Sequel">@L["Sequel"]</MudSelectItem>
<MudSelectItem T="Marechai.Data.SoftwareRelationshipType" Value="Marechai.Data.SoftwareRelationshipType.Fork">@L["Fork"]</MudSelectItem>
<MudSelectItem T="Marechai.Data.SoftwareRelationshipType" Value="Marechai.Data.SoftwareRelationshipType.Remake">@L["Remake"]</MudSelectItem>
<MudSelectItem T="Marechai.Data.SoftwareRelationshipType" Value="Marechai.Data.SoftwareRelationshipType.Remaster">@L["Remaster"]</MudSelectItem>
<MudSelectItem T="Marechai.Data.SoftwareRelationshipType" Value="Marechai.Data.SoftwareRelationshipType.Port">@L["Port"]</MudSelectItem>
</MudSelect>
@if(!string.IsNullOrWhiteSpace(_validationError))
{
<MudAlert Severity="Severity.Error" Class="mt-2">@_validationError</MudAlert>
}
</MudTabPanel>
@* Tab 2 - Included Software (versionless), edit mode only *@
@if(!IsNew)
{
<MudTabPanel Text="@L["Included Software"]" Icon="@Icons.Material.Filled.Apps">
@if(_includedSoftware is { Count: > 0 })
{
<MudList T="SoftwareBySoftwareCompilationDto" Dense="true">
@foreach(SoftwareBySoftwareCompilationDto sw in _includedSoftware)
{
<MudListItem T="SoftwareBySoftwareCompilationDto">
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween">
<MudText>@sw.SoftwareName</MudText>
<MudIconButton Icon="@Icons.Material.Filled.Delete" Size="Size.Small" Color="Color.Error"
OnClick="() => RemoveIncludedSoftware(sw)"/>
</MudStack>
</MudListItem>
}
</MudList>
}
<MudPaper Class="pa-4 mt-2" Elevation="1">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
<MudAutocomplete T="SoftwareDto"
Label="@L["Search Software"]"
@bind-Value="_selectedIncludedSoftware"
SearchFunc="SearchSoftware"
ToStringFunc="@(s => s?.Name ?? string.Empty)"
Variant="Variant.Outlined"
Clearable="true"
Style="flex: 1;"/>
<MudButton Variant="Variant.Filled" Color="Color.Primary" Size="Size.Small"
OnClick="AddIncludedSoftware"
Disabled="@(_selectedIncludedSoftware is null)">
@L["Add"]
</MudButton>
</MudStack>
</MudPaper>
</MudTabPanel>
@* Tab 3 - Included Software Versions, edit mode only *@
<MudTabPanel Text="@L["Included Versions"]" Icon="@Icons.Material.Filled.ListAlt">
@if(_includedVersions is { Count: > 0 })
{
<MudList T="SoftwareVersionBySoftwareCompilationDto" Dense="true">
@foreach(SoftwareVersionBySoftwareCompilationDto iv in _includedVersions)
{
<MudListItem T="SoftwareVersionBySoftwareCompilationDto">
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween">
<MudText>@iv.SoftwareName — @iv.SoftwareVersion</MudText>
<MudIconButton Icon="@Icons.Material.Filled.Delete" Size="Size.Small" Color="Color.Error"
OnClick="() => RemoveIncludedVersion(iv)"/>
</MudStack>
</MudListItem>
}
</MudList>
}
<MudPaper Class="pa-4 mt-2" Elevation="1">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
<MudAutocomplete T="SoftwareVersionDto"
Label="@L["Search Software Version"]"
@bind-Value="_selectedIncludedVersion"
SearchFunc="SearchVersions"
ToStringFunc="@(v => v is null ? string.Empty : $"{v.Software} — {v.VersionString}")"
Variant="Variant.Outlined"
Clearable="true"
Style="flex: 1;"/>
<MudButton Variant="Variant.Filled" Color="Color.Primary" Size="Size.Small"
OnClick="AddIncludedVersion"
Disabled="@(_selectedIncludedVersion is null)">
@L["Add"]
</MudButton>
</MudStack>
</MudPaper>
</MudTabPanel>
@* Tab 4 - Sub-compilations, edit mode only *@
<MudTabPanel Text="@L["Sub-Compilations"]" Icon="@Icons.Material.Filled.Collections">
@if(_includedCompilations is { Count: > 0 })
{
<MudList T="SoftwareCompilationDto" Dense="true">
@foreach(SoftwareCompilationDto sc in _includedCompilations)
{
<MudListItem T="SoftwareCompilationDto">
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween">
<MudText>@sc.Name</MudText>
<MudIconButton Icon="@Icons.Material.Filled.Delete" Size="Size.Small" Color="Color.Error"
OnClick="() => RemoveIncludedCompilation(sc)"/>
</MudStack>
</MudListItem>
}
</MudList>
}
<MudPaper Class="pa-4 mt-2" Elevation="1">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
<MudAutocomplete T="SoftwareCompilationDto"
Label="@L["Search Compilation"]"
@bind-Value="_selectedIncludedCompilation"
SearchFunc="SearchPredecessors"
ToStringFunc="@(c => c?.Name ?? string.Empty)"
Variant="Variant.Outlined"
Clearable="true"
Style="flex: 1;"/>
<MudButton Variant="Variant.Filled" Color="Color.Primary" Size="Size.Small"
OnClick="AddIncludedCompilation"
Disabled="@(_selectedIncludedCompilation is null)">
@L["Add"]
</MudButton>
</MudStack>
</MudPaper>
</MudTabPanel>
@* Tab 5 - Releases (read-only), edit mode only *@
<MudTabPanel Text="@L["Releases"]" Icon="@Icons.Material.Filled.NewReleases">
@if(_releases is { Count: > 0 })
{
<MudList T="SoftwareReleaseDto" Dense="true">
@foreach(SoftwareReleaseDto release in _releases)
{
<MudListItem T="SoftwareReleaseDto" Href="@($"/admin/software/releases")">
<MudText>
@(release.Platform ?? L["Unknown platform"])
@if(release.ReleaseDate is not null)
{
<MudText Typo="Typo.caption" Color="Color.Secondary" Class="ml-2">@release.ReleaseDate.Value.ToString("yyyy-MM-dd")</MudText>
}
</MudText>
</MudListItem>
}
</MudList>
}
else
{
<MudText Typo="Typo.body2" Color="Color.Secondary">@L["No releases yet. Add one from the Software Releases admin page and pick this compilation."]</MudText>
}
</MudTabPanel>
}
</MudTabs>
</DialogContent>
<DialogActions>
<MudButton OnClick="Cancel">@L["Cancel"]</MudButton>
<MudButton Color="Color.Primary" Variant="Variant.Filled" OnClick="Submit">@L["Save"]</MudButton>
</DialogActions>
</MudDialog>
@code {
string _validationError;
SoftwareDto _selectedSoftware;
MachineDto _selectedMachine;
SoftwareCompilationDto _selectedPredecessor;
List<SoftwareDto> _allSoftware;
List<MachineDto> _allMachines;
List<SoftwareBySoftwareCompilationDto> _includedSoftware;
SoftwareDto _selectedIncludedSoftware;
List<SoftwareVersionBySoftwareCompilationDto> _includedVersions;
SoftwareVersionDto _selectedIncludedVersion;
List<SoftwareVersionDto> _allVersionsForPicker;
List<SoftwareCompilationDto> _includedCompilations;
SoftwareCompilationDto _selectedIncludedCompilation;
List<SoftwareReleaseDto> _releases;
[CascadingParameter] IMudDialogInstance MudDialog { get; set; } = null!;
[Parameter] public bool IsNew { get; set; }
[Parameter] public int CompilationId { get; set; }
[Parameter] public string Name { get; set; } = string.Empty;
[Parameter] public int? SoftwareId { get; set; }
[Parameter] public string SoftwareName { get; set; }
[Parameter] public int? MachineId { get; set; }
[Parameter] public string MachineName { get; set; }
[Parameter] public int? PredecessorId { get; set; }
[Parameter] public string PredecessorName { get; set; }
[Parameter] public Marechai.Data.SoftwareRelationshipType RelationshipType { get; set; }
[Inject] SoftwareCompilationsService SoftwareCompilationsService { get; set; } = null!;
[Inject] SoftwareService SoftwareService { get; set; } = null!;
[Inject] SoftwareVersionsService SoftwareVersionsService { get; set; } = null!;
protected override async Task OnInitializedAsync()
{
if(SoftwareId is not null)
_selectedSoftware = new SoftwareDto { Id = SoftwareId, Name = SoftwareName ?? string.Empty };
if(MachineId is not null)
_selectedMachine = new MachineDto { Id = MachineId, Name = MachineName ?? string.Empty };
if(PredecessorId is not null)
_selectedPredecessor = new SoftwareCompilationDto { Id = PredecessorId, Name = PredecessorName ?? string.Empty };
_allSoftware = await SoftwareService.SearchSoftwareForPickerAsync(null, 50);
_allMachines = await SoftwareCompilationsService.GetAllMachinesAsync();
if(!IsNew && CompilationId > 0)
{
_includedSoftware = await SoftwareCompilationsService.GetIncludedSoftwareAsync(CompilationId);
_includedVersions = await SoftwareCompilationsService.GetIncludedVersionsAsync(CompilationId);
_includedCompilations = await SoftwareCompilationsService.GetIncludedCompilationsAsync(CompilationId);
_releases = await SoftwareCompilationsService.GetReleasesAsync(CompilationId);
_allVersionsForPicker = await SoftwareVersionsService.GetAllAsync();
}
else
{
_includedSoftware = [];
_includedVersions = [];
_includedCompilations = [];
_releases = [];
}
}
async Task<IEnumerable<SoftwareDto>> SearchSoftware(string value, CancellationToken ct)
{
List<SoftwareDto> results = await SoftwareService.SearchSoftwareForPickerAsync(value, 50, ct);
return results;
}
Task<IEnumerable<MachineDto>> SearchMachines(string value, CancellationToken ct)
{
IEnumerable<MachineDto> results = _allMachines ?? [];
if(!string.IsNullOrWhiteSpace(value))
results = (_allMachines ?? []).Where(m => m.Name?.Contains(value, StringComparison.OrdinalIgnoreCase) ?? false);
return Task.FromResult(results);
}
async Task<IEnumerable<SoftwareCompilationDto>> SearchPredecessors(string value, CancellationToken ct)
{
List<SoftwareCompilationDto> results = await SoftwareCompilationsService.GetPagedAsync(0, 50, value);
if(!IsNew && CompilationId > 0)
results = results.Where(c => c.Id != CompilationId).ToList();
return results;
}
Task<IEnumerable<SoftwareVersionDto>> SearchVersions(string value, CancellationToken ct)
{
IEnumerable<SoftwareVersionDto> results = _allVersionsForPicker ?? [];
if(!string.IsNullOrWhiteSpace(value))
{
results = (_allVersionsForPicker ?? []).Where(v =>
(v.Software?.Contains(value, StringComparison.OrdinalIgnoreCase) ?? false) ||
(v.VersionString?.Contains(value, StringComparison.OrdinalIgnoreCase) ?? false));
}
return Task.FromResult(results);
}
async Task AddIncludedSoftware()
{
if(_selectedIncludedSoftware?.Id is null) return;
var dto = new SoftwareBySoftwareCompilationDto
{
SoftwareCompilationId = CompilationId,
SoftwareId = _selectedIncludedSoftware.Id
};
(bool succeeded, string _) = await SoftwareCompilationsService.AddIncludedSoftwareAsync(dto);
if(succeeded)
{
_includedSoftware = await SoftwareCompilationsService.GetIncludedSoftwareAsync(CompilationId);
_selectedIncludedSoftware = null;
}
}
async Task RemoveIncludedSoftware(SoftwareBySoftwareCompilationDto sw)
{
await SoftwareCompilationsService.RemoveIncludedSoftwareAsync(CompilationId, sw.SoftwareId ?? 0);
_includedSoftware = await SoftwareCompilationsService.GetIncludedSoftwareAsync(CompilationId);
}
async Task AddIncludedVersion()
{
if(_selectedIncludedVersion?.Id is null) return;
var dto = new SoftwareVersionBySoftwareCompilationDto
{
SoftwareCompilationId = CompilationId,
SoftwareVersionId = _selectedIncludedVersion.Id
};
(bool succeeded, string _) = await SoftwareCompilationsService.AddIncludedVersionAsync(dto);
if(succeeded)
{
_includedVersions = await SoftwareCompilationsService.GetIncludedVersionsAsync(CompilationId);
_selectedIncludedVersion = null;
}
}
async Task RemoveIncludedVersion(SoftwareVersionBySoftwareCompilationDto iv)
{
await SoftwareCompilationsService.RemoveIncludedVersionAsync(CompilationId, iv.SoftwareVersionId ?? 0);
_includedVersions = await SoftwareCompilationsService.GetIncludedVersionsAsync(CompilationId);
}
async Task AddIncludedCompilation()
{
if(_selectedIncludedCompilation?.Id is null) return;
(bool succeeded, string _) =
await SoftwareCompilationsService.AddIncludedCompilationAsync(CompilationId, _selectedIncludedCompilation.Id.Value);
if(succeeded)
{
_includedCompilations = await SoftwareCompilationsService.GetIncludedCompilationsAsync(CompilationId);
_selectedIncludedCompilation = null;
}
}
async Task RemoveIncludedCompilation(SoftwareCompilationDto sc)
{
if(sc.Id is null) return;
await SoftwareCompilationsService.RemoveIncludedCompilationAsync(CompilationId, sc.Id.Value);
_includedCompilations = await SoftwareCompilationsService.GetIncludedCompilationsAsync(CompilationId);
}
void Cancel() => MudDialog.Cancel();
void Submit()
{
if(string.IsNullOrWhiteSpace(Name))
{
_validationError = L["Name is required."];
return;
}
MudDialog.Close(DialogResult.Ok(new SoftwareCompilationDialogResult
{
Name = Name,
SoftwareId = _selectedSoftware?.Id,
MachineId = _selectedMachine?.Id,
PredecessorId = _selectedPredecessor?.Id,
RelationshipType = RelationshipType
}));
}
}

View File

@@ -0,0 +1,10 @@
namespace Marechai.Pages.Admin;
public sealed class SoftwareCompilationDialogResult
{
public string Name { get; set; }
public int? SoftwareId { get; set; }
public int? MachineId { get; set; }
public int? PredecessorId { get; set; }
public Marechai.Data.SoftwareRelationshipType RelationshipType { get; set; }
}

View File

@@ -0,0 +1,57 @@
@page "/admin/software-compilations"
@attribute [Authorize(Roles = "Admin, UberAdmin")]
@using Marechai.ApiClient.Models
@using Marechai.Services
@inject SoftwareCompilationsService SoftwareCompilationsService
@inject IStringLocalizer<SoftwareCompilationsService> L
@inject IDialogService DialogService
@inject NavigationManager NavigationManager
<MudPaper Class="pa-6 mb-6" Elevation="0" Style="background: linear-gradient(135deg, #6a1b9a 0%, #ab47bc 100%); border-radius: 16px;">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="3">
<MudIcon Icon="@Icons.Material.Filled.Collections" Size="Size.Large" Style="color: white;"/>
<MudText Typo="Typo.h4" Style="color: white;">@L["Software Compilations Management"]</MudText>
</MudStack>
<MudText Typo="Typo.subtitle1" Style="color: rgba(255,255,255,0.85);" Class="mt-2">@L["Manage compilations, their bundling, predecessors, and contents."]</MudText>
</MudPaper>
@if(!string.IsNullOrWhiteSpace(_errorMessage))
{
<MudAlert Severity="Severity.Error" Class="mb-4" ShowCloseIcon="true" CloseIconClicked="() => _errorMessage = null">@_errorMessage</MudAlert>
}
@if(!string.IsNullOrWhiteSpace(_successMessage))
{
<MudAlert Severity="Severity.Success" Class="mb-4" ShowCloseIcon="true" CloseIconClicked="() => _successMessage = null">@_successMessage</MudAlert>
}
<MudPaper Elevation="2" Class="rounded-lg">
<MudToolBar Dense="true">
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Add" OnClick="OpenAddDialog">@L["Add Compilation"]</MudButton>
<MudSpacer/>
<MudTextField T="string" ValueChanged="OnSearch" Placeholder="@L["Search by name..."]"
Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search"
IconSize="Size.Medium" Class="mt-0" DebounceInterval="300" Immediate="true"/>
<MudIconButton Icon="@Icons.Material.Filled.Refresh" OnClick="async () => await _dataGrid.ReloadServerData()"/>
</MudToolBar>
<MudDataGrid @ref="_dataGrid" T="SoftwareCompilationDto" ServerData="ServerReload" Dense="true" Hover="true" Striped="true" Filterable="true" RowsPerPage="25">
<Columns>
<PropertyColumn Property="x => x.Name" Title="@L["Name"]"/>
<PropertyColumn Property="x => x.Software" Title="@L["Bundled with software"]"/>
<PropertyColumn Property="x => x.Machine" Title="@L["Bundled with machine"]"/>
<TemplateColumn Title="@L["Actions"]" Sortable="false" Filterable="false">
<CellTemplate>
<MudStack Row="true" Spacing="1">
<MudTooltip Text="@L["Edit"]"><MudIconButton Icon="@Icons.Material.Filled.Edit" Size="Size.Small" OnClick="() => OpenEditDialog(context.Item)"/></MudTooltip>
<MudTooltip Text="@L["View"]"><MudIconButton Icon="@Icons.Material.Filled.Visibility" Size="Size.Small" OnClick="() => NavigateToView(context.Item)"/></MudTooltip>
<MudTooltip Text="@L["Delete"]"><MudIconButton Icon="@Icons.Material.Filled.Delete" Size="Size.Small" Color="Color.Error" OnClick="() => ConfirmDelete(context.Item)"/></MudTooltip>
</MudStack>
</CellTemplate>
</TemplateColumn>
</Columns>
<PagerContent>
<MudDataGridPager T="SoftwareCompilationDto"/>
</PagerContent>
</MudDataGrid>
</MudPaper>

View File

@@ -0,0 +1,181 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Marechai.ApiClient.Models;
using MudBlazor;
namespace Marechai.Pages.Admin;
public partial class SoftwareCompilations
{
string _errorMessage;
string _searchText;
string _successMessage;
MudDataGrid<SoftwareCompilationDto> _dataGrid;
async Task<GridData<SoftwareCompilationDto>> ServerReload(GridState<SoftwareCompilationDto> state,
CancellationToken cancellationToken)
{
int skip = state.Page * state.PageSize;
int take = state.PageSize;
Task<int> countTask = SoftwareCompilationsService.GetCountAsync(_searchText);
Task<List<SoftwareCompilationDto>> dataTask = SoftwareCompilationsService.GetPagedAsync(skip, take, _searchText);
await Task.WhenAll(countTask, dataTask);
return new GridData<SoftwareCompilationDto>
{
Items = dataTask.Result,
TotalItems = countTask.Result
};
}
async Task OnSearch(string text)
{
_searchText = text;
await _dataGrid.ReloadServerData();
}
void NavigateToView(SoftwareCompilationDto compilation) =>
NavigationManager.NavigateTo($"/software-compilation/{compilation.Id}");
async Task OpenAddDialog()
{
DialogParameters<SoftwareCompilationDialog> parameters = new()
{
{ x => x.IsNew, true }
};
IDialogReference dialog = await DialogService.ShowAsync<SoftwareCompilationDialog>(L["Add Compilation"], parameters,
new DialogOptions
{
MaxWidth = MaxWidth.Large,
FullWidth = true
});
DialogResult result = await dialog.Result;
if(result is { Canceled: false, Data: SoftwareCompilationDialogResult data })
{
var dto = new SoftwareCompilationDto
{
Name = data.Name,
SoftwareId = data.SoftwareId,
MachineId = data.MachineId,
PredecessorId = data.PredecessorId,
RelationshipType = (int)data.RelationshipType
};
(bool succeeded, int? id, string errorMessage) = await SoftwareCompilationsService.CreateAsync(dto);
if(succeeded)
{
_successMessage = L["Compilation created successfully."];
await _dataGrid.ReloadServerData();
}
else
{
_errorMessage = errorMessage;
}
}
}
async Task OpenEditDialog(SoftwareCompilationDto compilation)
{
SoftwareCompilationDto full = await SoftwareCompilationsService.GetAsync(compilation.Id ?? 0);
if(full is null)
{
_errorMessage = L["Failed to load compilation details."];
return;
}
DialogParameters<SoftwareCompilationDialog> parameters = new()
{
{ x => x.IsNew, false },
{ x => x.CompilationId, full.Id ?? 0 },
{ x => x.Name, full.Name },
{ x => x.SoftwareId, full.SoftwareId },
{ x => x.SoftwareName, full.Software },
{ x => x.MachineId, full.MachineId },
{ x => x.MachineName, full.Machine },
{ x => x.PredecessorId, full.PredecessorId },
{ x => x.PredecessorName, full.Predecessor },
{ x => x.RelationshipType, (Marechai.Data.SoftwareRelationshipType)(full.RelationshipType ?? 0) }
};
IDialogReference dialog = await DialogService.ShowAsync<SoftwareCompilationDialog>(L["Edit Compilation"], parameters,
new DialogOptions
{
MaxWidth = MaxWidth.Large,
FullWidth = true
});
DialogResult result = await dialog.Result;
if(result is { Canceled: false, Data: SoftwareCompilationDialogResult data })
{
var dto = new SoftwareCompilationDto
{
Id = full.Id,
Name = data.Name,
SoftwareId = data.SoftwareId,
MachineId = data.MachineId,
PredecessorId = data.PredecessorId,
RelationshipType = (int)data.RelationshipType
};
(bool succeeded, string errorMessage) = await SoftwareCompilationsService.UpdateAsync(full.Id ?? 0, dto);
if(succeeded)
{
_successMessage = L["Compilation updated successfully."];
await _dataGrid.ReloadServerData();
}
else
{
_errorMessage = errorMessage;
}
}
}
async Task ConfirmDelete(SoftwareCompilationDto compilation)
{
DialogParameters<DeleteConfirmDialog> parameters = new()
{
{
x => x.ContentText,
string.Format(L["Are you sure you want to delete compilation '{0}'? This action cannot be undone."],
compilation.Name)
}
};
IDialogReference dialog =
await DialogService.ShowAsync<DeleteConfirmDialog>(L["Delete Compilation"], parameters,
new DialogOptions
{
MaxWidth = MaxWidth.ExtraSmall,
FullWidth = true
});
DialogResult result = await dialog.Result;
if(result is { Canceled: false })
{
(bool succeeded, string errorMessage) = await SoftwareCompilationsService.DeleteAsync(compilation.Id ?? 0);
if(succeeded)
{
_successMessage = L["Compilation deleted successfully."];
await _dataGrid.ReloadServerData();
}
else
{
_errorMessage = errorMessage;
}
}
}
}

View File

@@ -1017,7 +1017,6 @@
var dto = new SoftwareReleaseDto
{
Title = row.Title,
IsCompilation = false,
SoftwareId = SoftwareId,
SoftwareVersionId = row.MatchedVersion?.Id,
PlatformId = platformId,

View File

@@ -18,10 +18,17 @@
Class="mb-4"
HelperText="@L["Optional. Used as the display name for compilation releases."]"/>
<MudCheckBox @bind-Value="_isCompilation"
Label="@L["Is Compilation"]"
Disabled="@(!IsNew)"
Class="mb-4"/>
<MudAutocomplete T="SoftwareCompilationDto"
Label="@L["Compilation (optional)"]"
Value="_selectedCompilation"
ValueChanged="OnCompilationChanged"
SearchFunc="SearchCompilations"
ToStringFunc="@(c => c?.Name ?? string.Empty)"
Variant="Variant.Outlined"
Disabled="@(!IsNew)"
Clearable="true"
Class="mb-4"
HelperText="@L["Set this if the release is a compilation rather than a single piece of software."]"/>
@if(!_isCompilation)
{
@@ -374,101 +381,6 @@
</MudPaper>
</MudTabPanel>
@* Tab 7 - Included Software (compilations only) *@
@if(_isCompilation)
{
<MudTabPanel Text="@L["Included Software"]" Icon="@Icons.Material.Filled.LibraryBooks">
@* Versioned compilation section *@
@if(_includedSoftware is not { Count: > 0 })
{
<MudText Typo="Typo.subtitle2" Class="mb-2">@L["Included Software Versions"]</MudText>
@if(_includedVersions is { Count: > 0 })
{
<MudList T="SoftwareVersionBySoftwareReleaseDto" Dense="true">
@foreach(SoftwareVersionBySoftwareReleaseDto iv in _includedVersions)
{
<MudListItem T="SoftwareVersionBySoftwareReleaseDto">
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween">
<MudText>@iv.SoftwareName — @iv.SoftwareVersion</MudText>
<MudIconButton Icon="@Icons.Material.Filled.Delete"
Size="Size.Small"
Color="Color.Error"
OnClick="() => RemoveIncludedVersion(iv)"/>
</MudStack>
</MudListItem>
}
</MudList>
}
<MudPaper Class="pa-4 mt-2" Elevation="1">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
<MudAutocomplete T="SoftwareVersionDto"
Label="@L["Search Software Version"]"
@bind-Value="_selectedIncludedVersion"
SearchFunc="SearchAllVersions"
ToStringFunc="@(v => v is null ? string.Empty : $"{v.Software} — {v.VersionString}")"
Variant="Variant.Outlined"
Clearable="true"
Style="flex: 1;"/>
<MudButton Variant="Variant.Filled"
Color="Color.Primary"
Size="Size.Small"
OnClick="AddIncludedVersion"
Disabled="@(_selectedIncludedVersion is null)">
@L["Add"]
</MudButton>
</MudStack>
</MudPaper>
}
<MudDivider Class="my-4"/>
@* Versionless compilation section *@
@if(_includedVersions is not { Count: > 0 })
{
<MudText Typo="Typo.subtitle2" Class="mb-2">@L["Included Software (versionless)"]</MudText>
@if(_includedSoftware is { Count: > 0 })
{
<MudList T="SoftwareBySoftwareReleaseDto" Dense="true">
@foreach(SoftwareBySoftwareReleaseDto sw in _includedSoftware)
{
<MudListItem T="SoftwareBySoftwareReleaseDto">
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween">
<MudText>@sw.SoftwareName</MudText>
<MudIconButton Icon="@Icons.Material.Filled.Delete"
Size="Size.Small"
Color="Color.Error"
OnClick="() => RemoveIncludedSoftware(sw)"/>
</MudStack>
</MudListItem>
}
</MudList>
}
<MudPaper Class="pa-4 mt-2" Elevation="1">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
<MudAutocomplete T="SoftwareDto"
Label="@L["Search Software"]"
@bind-Value="_selectedIncludedSoftware"
SearchFunc="SearchSoftware"
ToStringFunc="@(s => s?.Name ?? string.Empty)"
Variant="Variant.Outlined"
Clearable="true"
Style="flex: 1;"/>
<MudButton Variant="Variant.Filled"
Color="Color.Primary"
Size="Size.Small"
OnClick="AddIncludedSoftware"
Disabled="@(_selectedIncludedSoftware is null)">
@L["Add"]
</MudButton>
</MudStack>
</MudPaper>
}
</MudTabPanel>
}
}
</MudTabs>
</DialogContent>
@@ -506,13 +418,9 @@
SoundSynthDto _selectedSoundSynth;
List<SoundSynthDto> _allSoundSynths;
bool _isCompilation;
List<SoftwareVersionBySoftwareReleaseDto> _includedVersions;
SoftwareVersionDto _selectedIncludedVersion;
List<SoftwareVersionDto> _allVersionsForPicker;
SoftwareCompilationDto _selectedCompilation;
bool _isCompilation => _selectedCompilation is not null;
List<SoftwareBySoftwareReleaseDto> _includedSoftware;
SoftwareDto _selectedIncludedSoftware;
List<SoftwareDto> _allSoftwareForPicker;
SoftwareDto _selectedSoftware;
@@ -530,20 +438,27 @@
[Parameter] public DateTime? ReleaseDate { get; set; }
[Parameter] public int ReleaseDatePrecision { get; set; }
[Parameter] public string Title { get; set; }
[Parameter] public bool IsCompilation { get; set; }
[Parameter] public int? SoftwareCompilationId { get; set; }
[Parameter] public string SoftwareCompilationName { get; set; }
[Parameter] public bool LockSoftwareSelection { get; set; }
[Parameter] public int? SoftwareId { get; set; }
[Parameter] public string SoftwareName { get; set; }
[Parameter] public int? SoftwareVersionId { get; set; }
[Inject] SoftwareReleasesService SoftwareReleasesService { get; set; } = null!;
[Inject] SoftwareCompilationsService SoftwareCompilationsService { get; set; } = null!;
bool IsSoftwareSelectionLocked => LockSoftwareSelection && !_isCompilation && SoftwareId is not null;
bool ShouldShowVersionPicker => !_isCompilation && (_selectedSoftware is not null || IsSoftwareSelectionLocked);
protected override async Task OnInitializedAsync()
{
_isCompilation = IsCompilation;
if(SoftwareCompilationId is not null)
{
_selectedCompilation = await SoftwareCompilationsService.GetAsync(SoftwareCompilationId.Value) ??
new SoftwareCompilationDto { Id = SoftwareCompilationId, Name = SoftwareCompilationName };
}
bool shouldLoadSoftwarePicker = _isCompilation || !IsSoftwareSelectionLocked;
// Load independent reference data in parallel to reduce dialog startup latency.
@@ -586,34 +501,13 @@
Task<List<GpuBySoftwareReleaseDto>> recommendedGpusTask = SoftwareReleasesService.GetRecommendedGpusAsync(ReleaseId);
Task<List<SoundSynthBySoftwareReleaseDto>> soundSynthsTask = SoftwareReleasesService.GetSoundSynthsAsync(ReleaseId);
if(_isCompilation)
{
Task<List<SoftwareVersionBySoftwareReleaseDto>> includedVersionsTask = SoftwareReleasesService.GetIncludedVersionsAsync(ReleaseId);
Task<List<SoftwareBySoftwareReleaseDto>> includedSoftwareTask = SoftwareReleasesService.GetIncludedSoftwareAsync(ReleaseId);
await Task.WhenAll(releaseRegionsTask,
releaseLanguagesTask,
barcodesTask,
productCodesTask,
minimumGpusTask,
recommendedGpusTask,
soundSynthsTask,
includedVersionsTask,
includedSoftwareTask);
_includedVersions = includedVersionsTask.Result;
_includedSoftware = includedSoftwareTask.Result;
}
else
{
await Task.WhenAll(releaseRegionsTask,
releaseLanguagesTask,
barcodesTask,
productCodesTask,
minimumGpusTask,
recommendedGpusTask,
soundSynthsTask);
}
await Task.WhenAll(releaseRegionsTask,
releaseLanguagesTask,
barcodesTask,
productCodesTask,
minimumGpusTask,
recommendedGpusTask,
soundSynthsTask);
_releaseRegions = releaseRegionsTask.Result;
_releaseLanguages = releaseLanguagesTask.Result;
@@ -632,8 +526,6 @@
_minimumGpus = [];
_recommendedGpus = [];
_soundSynths = [];
_includedVersions = [];
_includedSoftware = [];
}
if(PublisherId is not null && _allCompanies is not null)
@@ -650,9 +542,6 @@
if(SoftwareVersionId is not null)
_selectedVersion = _allVersionsForSelectedSoftware.Find(v => v.Id == SoftwareVersionId);
}
if(_isCompilation)
_allVersionsForPicker = await SoftwareReleasesService.GetAllSoftwareVersionsForPickerAsync();
}
Task<IEnumerable<SoftwarePlatformDto>> SearchPlatforms(string value, CancellationToken ct)
@@ -889,22 +778,6 @@
_soundSynths = await SoftwareReleasesService.GetSoundSynthsAsync(ReleaseId);
}
// ── Included version management (compilations only) ──
Task<IEnumerable<SoftwareVersionDto>> SearchAllVersions(string value, CancellationToken ct)
{
IEnumerable<SoftwareVersionDto> results = _allVersionsForPicker ?? [];
if(!string.IsNullOrWhiteSpace(value))
{
results = (_allVersionsForPicker ?? []).Where(v =>
(v.Software?.Contains(value, StringComparison.OrdinalIgnoreCase) ?? false) ||
(v.VersionString?.Contains(value, StringComparison.OrdinalIgnoreCase) ?? false));
}
return Task.FromResult(results);
}
Task<IEnumerable<SoftwareDto>> SearchSoftware(string value, CancellationToken ct)
{
IEnumerable<SoftwareDto> results = _allSoftwareForPicker ?? [];
@@ -916,6 +789,15 @@
return Task.FromResult(results);
}
async Task<IEnumerable<SoftwareCompilationDto>> SearchCompilations(string value, CancellationToken ct)
{
List<SoftwareCompilationDto> results = await SoftwareCompilationsService.GetPagedAsync(0, 20, value);
return results;
}
void OnCompilationChanged(SoftwareCompilationDto compilation) => _selectedCompilation = compilation;
Task<IEnumerable<SoftwareVersionDto>> SearchVersionsForSoftware(string value, CancellationToken ct)
{
IEnumerable<SoftwareVersionDto> results = _allVersionsForSelectedSoftware ?? [];
@@ -946,58 +828,6 @@
_allVersionsForSelectedSoftware = await SoftwareReleasesService.GetVersionsBySoftwareAsync(softwareId);
}
async Task AddIncludedVersion()
{
if(_selectedIncludedVersion is null) return;
var dto = new SoftwareVersionBySoftwareReleaseDto
{
ReleaseId = ReleaseId,
SoftwareVersionId = _selectedIncludedVersion.Id
};
(bool succeeded, string _) = await SoftwareReleasesService.AddIncludedVersionAsync(dto);
if(succeeded)
{
_includedVersions = await SoftwareReleasesService.GetIncludedVersionsAsync(ReleaseId);
_selectedIncludedVersion = null;
}
}
async Task RemoveIncludedVersion(SoftwareVersionBySoftwareReleaseDto iv)
{
await SoftwareReleasesService.RemoveIncludedVersionAsync(iv.ReleaseId ?? 0, iv.SoftwareVersionId ?? 0);
_includedVersions = await SoftwareReleasesService.GetIncludedVersionsAsync(ReleaseId);
}
// ── Included software management (versionless compilations only) ──
async Task AddIncludedSoftware()
{
if(_selectedIncludedSoftware is null) return;
var dto = new SoftwareBySoftwareReleaseDto
{
ReleaseId = ReleaseId,
SoftwareId = _selectedIncludedSoftware.Id
};
(bool succeeded, string _) = await SoftwareReleasesService.AddIncludedSoftwareAsync(dto);
if(succeeded)
{
_includedSoftware = await SoftwareReleasesService.GetIncludedSoftwareAsync(ReleaseId);
_selectedIncludedSoftware = null;
}
}
async Task RemoveIncludedSoftware(SoftwareBySoftwareReleaseDto sw)
{
await SoftwareReleasesService.RemoveIncludedSoftwareAsync(sw.ReleaseId ?? 0, sw.SoftwareId ?? 0);
_includedSoftware = await SoftwareReleasesService.GetIncludedSoftwareAsync(ReleaseId);
}
void Cancel() => MudDialog.Cancel();
void Submit()
@@ -1005,7 +835,7 @@
MudDialog.Close(DialogResult.Ok(new SoftwareReleaseDialogResult
{
Title = Title,
IsCompilation = _isCompilation,
SoftwareCompilationId = _selectedCompilation?.Id,
SoftwareId = _isCompilation ? null : (IsSoftwareSelectionLocked ? SoftwareId : (int?)_selectedSoftware?.Id),
SoftwareVersionId = _isCompilation ? null : (int?)_selectedVersion?.Id,
PlatformId = _selectedPlatform?.Id,

View File

@@ -6,7 +6,7 @@ namespace Marechai.Pages.Admin;
public sealed class SoftwareReleaseDialogResult
{
public string Title { get; set; }
public bool IsCompilation { get; set; }
public int? SoftwareCompilationId { get; set; }
public int? SoftwareId { get; set; }
public int? SoftwareVersionId { get; set; }
public int? PlatformId { get; set; }

View File

@@ -31,6 +31,7 @@
@inject SoftwareReleasesService SoftwareReleasesService
@inject SoftwareService SoftwareService
@inject SoftwarePlatformsService SoftwarePlatformsService
@inject SoftwareCompilationsService SoftwareCompilationsService
@inject IStringLocalizer<SoftwareReleasesService> L
@inject IJSRuntime JsRuntime
@@ -909,16 +910,40 @@
}
}
// Create the backing compilation if this row is a compilation
int? compilationId = null;
if(row.IsCompilation == true)
{
string compName = !string.IsNullOrWhiteSpace(row.Title) ? row.Title.Trim() : L["Compilation"];
var compDto = new SoftwareCompilationDto { Name = compName };
(bool compSucceeded, int? newCompId, string compError) = await SoftwareCompilationsService.CreateAsync(compDto);
if(compSucceeded)
{
compilationId = newCompId;
}
else
{
_failedCount++;
row.ImportError = string.Format(L["Failed to create compilation '{0}': {1}"], compName, compError ?? L["Unknown error"]);
_importRows.Add(row);
await UpdateProgress();
continue;
}
}
// Build the release DTO
var dto = new SoftwareReleaseDto
{
Title = row.Title,
IsCompilation = row.IsCompilation ?? false,
SoftwareId = row.IsCompilation == true ? null : softwareId,
PlatformId = platformId,
PublisherId = row.SelectedPublisher?.Id,
ReleaseDatePrecision = row.ReleaseDatePrecision,
ReleaseDate = row.ReleaseDate.HasValue ? new DateTimeOffset(row.ReleaseDate.Value) : null
Title = row.Title,
SoftwareCompilationId = compilationId,
SoftwareId = row.IsCompilation == true ? null : softwareId,
PlatformId = platformId,
PublisherId = row.SelectedPublisher?.Id,
ReleaseDatePrecision = row.ReleaseDatePrecision,
ReleaseDate = row.ReleaseDate.HasValue ? new DateTimeOffset(row.ReleaseDate.Value) : null
};
(int? id, string error) = await SoftwareReleasesService.CreateAsync(dto);

View File

@@ -135,13 +135,10 @@ public partial class SoftwareReleases
async Task OpenAddDialog()
{
bool isCompilation = !_isVersionContext && !_isSoftwareContext;
DialogParameters<SoftwareReleaseDialog> parameters = new()
{
{ x => x.IsNew, true },
{ x => x.ParentVersionId, VersionId },
{ x => x.IsCompilation, isCompilation },
{ x => x.LockSoftwareSelection, _isSoftwareContext || _isVersionContext },
{ x => x.SoftwareName, _parentSoftwareName },
{ x => x.SoftwareId, _isSoftwareContext ? SoftwareId : (_isVersionContext ? _parentSoftwareId : null) },
@@ -163,7 +160,7 @@ public partial class SoftwareReleases
var dto = new SoftwareReleaseDto
{
Title = data.Title,
IsCompilation = data.IsCompilation,
SoftwareCompilationId = data.SoftwareCompilationId,
SoftwareId = data.SoftwareId,
SoftwareVersionId = data.SoftwareVersionId,
PlatformId = data.PlatformId,
@@ -248,7 +245,8 @@ public partial class SoftwareReleases
{ x => x.ReleaseId, full.Id ?? 0 },
{ x => x.ParentVersionId, VersionId },
{ x => x.Title, full.Title },
{ x => x.IsCompilation, full.IsCompilation == true },
{ x => x.SoftwareCompilationId, full.SoftwareCompilationId },
{ x => x.SoftwareCompilationName, full.SoftwareCompilation },
{ x => x.LockSoftwareSelection, _isSoftwareContext || _isVersionContext },
{ x => x.SoftwareName, _parentSoftwareName ?? full.Software },
{ x => x.SoftwareId, full.SoftwareId },
@@ -275,7 +273,7 @@ public partial class SoftwareReleases
{
Id = full.Id,
Title = data.Title,
IsCompilation = data.IsCompilation,
SoftwareCompilationId = full.SoftwareCompilationId,
SoftwareId = full.SoftwareId,
SoftwareVersionId = data.SoftwareVersionId,
PlatformId = data.PlatformId,

View File

@@ -75,7 +75,7 @@
{
<MudIcon Icon="@Icons.Material.Filled.NewReleases" Size="Size.Large" Style="color: white;"/>
}
@if(_release.IsCompilation == true)
@if(_release.SoftwareCompilationId is not null)
{
<MudText Typo="Typo.h4" Align="Align.Center" Style="color: white; font-weight: 700;">@(!string.IsNullOrEmpty(_release.Title) ? _release.Title : L["Compilation"])</MudText>
<MudChip T="string" Variant="Variant.Filled" Size="Size.Small" Style="background-color: #9C27B0; color: white;">@L["Compilation"]</MudChip>
@@ -190,7 +190,7 @@
<td>@_release.SoftwareVersion</td>
</tr>
}
@if(!string.IsNullOrEmpty(_release.Title) && _release.IsCompilation != true)
@if(!string.IsNullOrEmpty(_release.Title) && _release.SoftwareCompilationId is null)
{
<tr>
<td style="width: 33%; text-align: right;"><strong>@L["Title"]</strong></td>
@@ -246,46 +246,6 @@
</MudCardContent>
</MudCard>
@* ── Included Software Card (compilations only) ── *@
@if(_release.IsCompilation == true && (_includedVersions.Count > 0 || _includedSoftware.Count > 0))
{
<MudExpansionPanels Elevation="2" Class="mb-4 rounded-lg">
<MudExpansionPanel>
<TitleContent>
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
<MudIcon Icon="@Icons.Material.Filled.LibraryBooks" Color="Color.Primary"/>
<MudText Typo="Typo.h6">@L["Included Software"]</MudText>
<MudChip T="string" Size="Size.Small" Color="Color.Success">@(_includedVersions.Count + _includedSoftware.Count)</MudChip>
</MudStack>
</TitleContent>
<ChildContent>
@if(_includedVersions.Count > 0)
{
<MudList T="SoftwareVersionBySoftwareReleaseDto" Dense="true">
@foreach(SoftwareVersionBySoftwareReleaseDto iv in _includedVersions)
{
<MudListItem T="SoftwareVersionBySoftwareReleaseDto" Icon="@Icons.Material.Filled.Apps">
<MudText>@iv.SoftwareName — @iv.SoftwareVersion</MudText>
</MudListItem>
}
</MudList>
}
@if(_includedSoftware.Count > 0)
{
<MudList T="SoftwareBySoftwareReleaseDto" Dense="true">
@foreach(SoftwareBySoftwareReleaseDto sw in _includedSoftware)
{
<MudListItem T="SoftwareBySoftwareReleaseDto" Icon="@Icons.Material.Filled.Apps">
<MudText>@sw.SoftwareName</MudText>
</MudListItem>
}
</MudList>
}
</ChildContent>
</MudExpansionPanel>
</MudExpansionPanels>
}
@* ── Barcodes Card ── *@
@if(_barcodes.Count > 0)
{

View File

@@ -45,8 +45,6 @@ public partial class ReleaseView
List<SoftwareAttributeDto> _releaseRatings = [];
List<SoftwareBarcodeDto> _barcodes = [];
int _lastId;
List<SoftwareVersionBySoftwareReleaseDto> _includedVersions = [];
List<SoftwareBySoftwareReleaseDto> _includedSoftware = [];
bool _isCollected;
bool _loaded;
List<GpuBySoftwareReleaseDto> _minimumGpus = [];
@@ -133,13 +131,7 @@ public partial class ReleaseView
SoftwareVersionDto version = await Service.GetVersionByIdAsync(_release.SoftwareVersionId.Value);
_softwareName = version?.Software;
}
else if(_release.IsCompilation == true)
{
// Compilation: load included versions and/or software
_includedVersions = await Service.GetIncludedVersionsAsync(Id);
_includedSoftware = await Service.GetIncludedSoftwareAsync(Id);
}
else
else if(_release.SoftwareCompilationId is null)
{
// Versionless single release: use software name from DTO
_softwareName = _release.Software;

View File

@@ -127,7 +127,7 @@
<div id="@_scrollerId" style="height: calc(100dvh - 280px); min-height: 360px; overflow-y: auto;">
<MudList T="SoftwareDto" Dense="true">
<Virtualize Items="_software" Context="sw" ItemSize="100">
<MudListItem T="SoftwareDto" Href="@(sw.IsCompilation == true ? $"/software/release/{sw.Id}" : $"/software/{sw.Id}")">
<MudListItem T="SoftwareDto" Href="@(sw.IsCompilation == true ? $"/software-compilation/{sw.Id}" : $"/software/{sw.Id}")">
<div class="d-flex align-center gap-3">
@if(sw.FrontCoverId.HasValue)
{

View File

@@ -763,7 +763,7 @@ else
{
<MudListItem T="SoftwareReleaseDto" Href="@($"/software/release/{release.Id}")" Icon="@Icons.Material.Filled.NewReleases">
<div class="d-flex flex-wrap align-center gap-2">
@if(!string.IsNullOrEmpty(release.Title) && release.IsCompilation != true)
@if(!string.IsNullOrEmpty(release.Title) && release.SoftwareCompilationId is null)
{
<MudText Typo="Typo.body2" Style="font-style: italic;">@release.Title</MudText>
}

View File

@@ -0,0 +1,132 @@
@page "/software-compilation/{Id:int}"
@using Marechai.ApiClient.Models
@using Marechai.Services
@inject SoftwareCompilationsService Service
@inject IStringLocalizer<SoftwareCompilationsService> L
@inject ApiAssetUrlProvider AssetUrls
@if(_loaded && _compilation is null)
{
<MudAlert Severity="Severity.Warning">@L["Compilation not found."]</MudAlert>
}
else if(_compilation is not null)
{
<MudPaper Class="pa-6 mb-6" Elevation="0" Style="background: linear-gradient(135deg, #6a1b9a 0%, #ab47bc 100%); border-radius: 16px;">
<MudStack AlignItems="AlignItems.Center" Spacing="3">
@if(_frontCoverId.HasValue)
{
<picture>
<source srcset="@(AssetUrls.BaseUrl)/assets/photos/software-covers/avif/4k/@(_frontCoverId).avif" type="image/avif">
<source srcset="@(AssetUrls.BaseUrl)/assets/photos/software-covers/webp/4k/@(_frontCoverId).webp" type="image/webp">
<img alt="@_compilation.Name"
src="@(AssetUrls.BaseUrl)/assets/photos/software-covers/jpeg/4k/@(_frontCoverId).jpg"
style="max-height: 256px; object-fit: contain; border-radius: 8px; box-shadow: 0 4px 16px rgba(0,0,0,0.3);"/>
</picture>
}
else
{
<MudIcon Icon="@Icons.Material.Filled.Collections" Size="Size.Large" Style="color: white;"/>
}
<MudText Typo="Typo.h4" Align="Align.Center" Style="color: white; font-weight: 700;">@_compilation.Name</MudText>
<MudChip T="string" Variant="Variant.Filled" Size="Size.Small" Style="background-color: #9C27B0; color: white;">@L["Compilation"]</MudChip>
</MudStack>
</MudPaper>
@if(!string.IsNullOrEmpty(_compilation.Software) || !string.IsNullOrEmpty(_compilation.Machine))
{
<MudCard Elevation="2" Class="mb-4 rounded-lg">
<MudCardContent>
<MudText Typo="Typo.h6" Class="mb-2">@L["Bundled with"]</MudText>
@if(!string.IsNullOrEmpty(_compilation.Software))
{
<MudLink Href="@($"/software/{_compilation.SoftwareId}")">@_compilation.Software</MudLink>
}
@if(!string.IsNullOrEmpty(_compilation.Machine))
{
<MudLink Href="@($"/machine/{_compilation.MachineId}")">@_compilation.Machine</MudLink>
}
</MudCardContent>
</MudCard>
}
@if(_releases is { Count: > 0 })
{
<MudExpansionPanels Elevation="2" Class="mb-4 rounded-lg">
<MudExpansionPanel>
<TitleContent>
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
<MudIcon Icon="@Icons.Material.Filled.NewReleases" Color="Color.Primary"/>
<MudText Typo="Typo.h6">@L["Releases"]</MudText>
<MudChip T="string" Size="Size.Small" Color="Color.Success">@_releases.Count</MudChip>
</MudStack>
</TitleContent>
<ChildContent>
<MudList T="SoftwareReleaseDto" Dense="true">
@foreach(SoftwareReleaseDto release in _releases)
{
<MudListItem T="SoftwareReleaseDto" Href="@($"/software/release/{release.Id}")" Icon="@Icons.Material.Filled.NewReleases">
<MudText>
@(release.Platform ?? L["Unknown platform"])
@if(release.ReleaseDate is not null)
{
<MudText Typo="Typo.caption" Color="Color.Secondary" Class="ml-2">@release.ReleaseDate.Value.ToString("yyyy-MM-dd")</MudText>
}
</MudText>
</MudListItem>
}
</MudList>
</ChildContent>
</MudExpansionPanel>
</MudExpansionPanels>
}
@if((_includedSoftware is { Count: > 0 }) || (_includedVersions is { Count: > 0 }) || (_includedCompilations is { Count: > 0 }))
{
<MudExpansionPanels Elevation="2" Class="mb-4 rounded-lg">
<MudExpansionPanel>
<TitleContent>
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
<MudIcon Icon="@Icons.Material.Filled.LibraryBooks" Color="Color.Primary"/>
<MudText Typo="Typo.h6">@L["Included Software"]</MudText>
<MudChip T="string" Size="Size.Small" Color="Color.Success">@(_includedSoftware.Count + _includedVersions.Count + _includedCompilations.Count)</MudChip>
</MudStack>
</TitleContent>
<ChildContent>
@if(_includedVersions is { Count: > 0 })
{
<MudList T="SoftwareVersionBySoftwareCompilationDto" Dense="true">
@foreach(SoftwareVersionBySoftwareCompilationDto iv in _includedVersions)
{
<MudListItem T="SoftwareVersionBySoftwareCompilationDto" Icon="@Icons.Material.Filled.Apps">
<MudText>@iv.SoftwareName — @iv.SoftwareVersion</MudText>
</MudListItem>
}
</MudList>
}
@if(_includedSoftware is { Count: > 0 })
{
<MudList T="SoftwareBySoftwareCompilationDto" Dense="true">
@foreach(SoftwareBySoftwareCompilationDto sw in _includedSoftware)
{
<MudListItem T="SoftwareBySoftwareCompilationDto" Icon="@Icons.Material.Filled.Apps">
<MudText>@sw.SoftwareName</MudText>
</MudListItem>
}
</MudList>
}
@if(_includedCompilations is { Count: > 0 })
{
<MudList T="SoftwareCompilationDto" Dense="true">
@foreach(SoftwareCompilationDto sc in _includedCompilations)
{
<MudListItem T="SoftwareCompilationDto" Href="@($"/software-compilation/{sc.Id}")" Icon="@Icons.Material.Filled.Collections">
<MudText>@sc.Name</MudText>
</MudListItem>
}
</MudList>
}
</ChildContent>
</MudExpansionPanel>
</MudExpansionPanels>
}
}

View File

@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Marechai.ApiClient.Models;
using Microsoft.AspNetCore.Components;
namespace Marechai.Pages.SoftwareCompilations;
public partial class View
{
SoftwareCompilationDto _compilation;
Guid? _frontCoverId;
List<SoftwareReleaseDto> _releases = [];
List<SoftwareBySoftwareCompilationDto> _includedSoftware = [];
List<SoftwareVersionBySoftwareCompilationDto> _includedVersions = [];
List<SoftwareCompilationDto> _includedCompilations = [];
bool _loaded;
[Parameter] public int Id { get; set; }
protected override async Task OnParametersSetAsync()
{
_loaded = false;
_compilation = await Service.GetAsync(Id);
if(_compilation is not null)
{
_frontCoverId = _compilation.FrontCoverId;
_releases = await Service.GetReleasesAsync(Id);
_includedSoftware = await Service.GetIncludedSoftwareAsync(Id);
_includedVersions = await Service.GetIncludedVersionsAsync(Id);
_includedCompilations = await Service.GetIncludedCompilationsAsync(Id);
}
_loaded = true;
}
}

View File

@@ -535,7 +535,7 @@
? CurrentDto.Software
: $"{CurrentDto.Software} {CurrentDto.SoftwareVersion}")
: null;
if(CurrentDto.IsCompilation == true)
if(CurrentDto.SoftwareCompilationId is not null)
parent = string.IsNullOrEmpty(parent) ? L["Compilation"] : $"{parent} ({L["Compilation"]})";
_parentLabel = parent;
}

View File

@@ -72,6 +72,7 @@ public static class Register
services.AddScoped<SoftwareAttributesService>();
services.AddScoped<SoftwareVersionsService>();
services.AddScoped<SoftwareReleasesService>();
services.AddScoped<SoftwareCompilationsService>();
services.AddScoped<SmartphonesService>();
services.AddScoped<PdasService>();
services.AddScoped<TabletsService>();

View File

@@ -0,0 +1,338 @@
/******************************************************************************
// MARECHAI: Master repository of computing history artifacts information
// ----------------------------------------------------------------------------
//
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// --[ License ] --------------------------------------------------------------
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2003-2026 Natalia Portillo
*******************************************************************************/
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Marechai.ApiClient.Models;
using Microsoft.Kiota.Abstractions;
namespace Marechai.Services;
public class SoftwareCompilationsService(Marechai.ApiClient.Client client)
{
static string ExtractDetail(ApiException ex) => ex.Message;
public async Task<List<SoftwareCompilationDto>> GetPagedAsync(int skip, int take, string search = null)
{
try
{
List<SoftwareCompilationDto> compilations = await client.SoftwareCompilations.GetAsync(config =>
{
config.QueryParameters.Skip = skip;
config.QueryParameters.Take = take;
config.QueryParameters.Search = search;
});
return compilations ?? [];
}
catch
{
return [];
}
}
public async Task<int> GetCountAsync(string search = null)
{
try
{
int? count = await client.SoftwareCompilations.Count.GetAsync(config =>
{
config.QueryParameters.Search = search;
});
return count ?? 0;
}
catch
{
return 0;
}
}
public async Task<SoftwareCompilationDto> GetAsync(int id)
{
try
{
return await client.SoftwareCompilations[id].GetAsync();
}
catch
{
return null;
}
}
public async Task<(bool succeeded, int? id, string error)> CreateAsync(SoftwareCompilationDto dto)
{
try
{
int? id = await client.SoftwareCompilations.PostAsync(dto);
return (true, id, null);
}
catch(ApiException ex)
{
return (false, null, ExtractDetail(ex));
}
catch(Exception ex)
{
return (false, null, ex.Message);
}
}
public async Task<(bool succeeded, string error)> UpdateAsync(int id, SoftwareCompilationDto dto)
{
try
{
await client.SoftwareCompilations[id].PutAsync(dto);
return (true, null);
}
catch(ApiException ex)
{
return (false, ExtractDetail(ex));
}
catch(Exception ex)
{
return (false, ex.Message);
}
}
public async Task<(bool succeeded, string error)> DeleteAsync(int id)
{
try
{
await client.SoftwareCompilations[id].DeleteAsync();
return (true, null);
}
catch(ApiException ex)
{
return (false, ExtractDetail(ex));
}
catch(Exception ex)
{
return (false, ex.Message);
}
}
public async Task<List<SoftwareReleaseDto>> GetReleasesAsync(int id)
{
try
{
List<SoftwareReleaseDto> releases = await client.SoftwareCompilations[id].Releases.GetAsync();
return releases ?? [];
}
catch
{
return [];
}
}
public async Task<List<MachineDto>> GetAllMachinesAsync()
{
try
{
List<MachineDto> machines = await client.Machines.GetAsync();
return machines ?? [];
}
catch
{
return [];
}
}
public async Task<List<SoftwareCoverDto>> GetCoversAsync(int id)
{
try
{
List<SoftwareCoverDto> covers = await client.SoftwareCompilations[id].Covers.GetAsync();
return covers ?? [];
}
catch
{
return [];
}
}
// ── Included software / version junction methods ──
public async Task<List<SoftwareBySoftwareCompilationDto>> GetIncludedSoftwareAsync(int id)
{
try
{
List<SoftwareBySoftwareCompilationDto> software = await client.SoftwareCompilations[id].Software.GetAsync();
return software ?? [];
}
catch
{
return [];
}
}
public async Task<(bool succeeded, string error)> AddIncludedSoftwareAsync(SoftwareBySoftwareCompilationDto dto)
{
try
{
await client.SoftwareCompilations[(int)(dto.SoftwareCompilationId ?? 0)].Software
.PostAsync(dto);
return (true, null);
}
catch(ApiException ex)
{
return (false, ExtractDetail(ex));
}
catch(Exception ex)
{
return (false, ex.Message);
}
}
public async Task<(bool succeeded, string error)> RemoveIncludedSoftwareAsync(int id, int softwareId)
{
try
{
await client.SoftwareCompilations[id].Software[softwareId].DeleteAsync();
return (true, null);
}
catch(ApiException ex)
{
return (false, ExtractDetail(ex));
}
catch(Exception ex)
{
return (false, ex.Message);
}
}
public async Task<List<SoftwareVersionBySoftwareCompilationDto>> GetIncludedVersionsAsync(int id)
{
try
{
List<SoftwareVersionBySoftwareCompilationDto> versions =
await client.SoftwareCompilations[id].Versions.GetAsync();
return versions ?? [];
}
catch
{
return [];
}
}
public async Task<(bool succeeded, string error)> AddIncludedVersionAsync(SoftwareVersionBySoftwareCompilationDto dto)
{
try
{
await client.SoftwareCompilations[(int)(dto.SoftwareCompilationId ?? 0)].Versions
.PostAsync(dto);
return (true, null);
}
catch(ApiException ex)
{
return (false, ExtractDetail(ex));
}
catch(Exception ex)
{
return (false, ex.Message);
}
}
public async Task<(bool succeeded, string error)> RemoveIncludedVersionAsync(int id, int versionId)
{
try
{
await client.SoftwareCompilations[id].Versions[versionId].DeleteAsync();
return (true, null);
}
catch(ApiException ex)
{
return (false, ExtractDetail(ex));
}
catch(Exception ex)
{
return (false, ex.Message);
}
}
// ── Nested-compilation junction methods ──
public async Task<List<SoftwareCompilationDto>> GetIncludedCompilationsAsync(int id)
{
try
{
List<SoftwareCompilationDto> compilations = await client.SoftwareCompilations[id].Compilations.GetAsync();
return compilations ?? [];
}
catch
{
return [];
}
}
public async Task<(bool succeeded, string error)> AddIncludedCompilationAsync(int id, int childId)
{
try
{
await client.SoftwareCompilations[id].Compilations[childId].PostAsync();
return (true, null);
}
catch(ApiException ex)
{
return (false, ExtractDetail(ex));
}
catch(Exception ex)
{
return (false, ex.Message);
}
}
public async Task<(bool succeeded, string error)> RemoveIncludedCompilationAsync(int id, int childId)
{
try
{
await client.SoftwareCompilations[id].Compilations[childId].DeleteAsync();
return (true, null);
}
catch(ApiException ex)
{
return (false, ExtractDetail(ex));
}
catch(Exception ex)
{
return (false, ex.Message);
}
}
}

View File

@@ -535,59 +535,6 @@ public class SoftwareReleasesService(Marechai.ApiClient.Client client, Reference
}
}
// ── Compilation junction methods ──
public async Task<List<SoftwareVersionBySoftwareReleaseDto>> GetIncludedVersionsAsync(int releaseId)
{
try
{
List<SoftwareVersionBySoftwareReleaseDto> versions =
await client.Software.Releases[releaseId].Versions.GetAsync();
return versions ?? [];
}
catch
{
return [];
}
}
public async Task<(bool succeeded, string error)> AddIncludedVersionAsync(SoftwareVersionBySoftwareReleaseDto dto)
{
try
{
await client.Software.Releases[(int)(dto.ReleaseId ?? 0)].Versions.PostAsync(dto);
return (true, null);
}
catch(ApiException ex)
{
return (false, ExtractDetail(ex));
}
catch(Exception ex)
{
return (false, ex.Message);
}
}
public async Task<(bool succeeded, string error)> RemoveIncludedVersionAsync(int releaseId, int versionId)
{
try
{
await client.Software.Releases[releaseId].Versions[versionId].DeleteAsync();
return (true, null);
}
catch(ApiException ex)
{
return (false, ExtractDetail(ex));
}
catch(Exception ex)
{
return (false, ex.Message);
}
}
public async Task<List<SoftwareVersionDto>> GetAllSoftwareVersionsForPickerAsync()
{
try
@@ -748,59 +695,6 @@ public class SoftwareReleasesService(Marechai.ApiClient.Client client, Reference
}
}
// ── Versionless compilation junction methods ──
public async Task<List<SoftwareBySoftwareReleaseDto>> GetIncludedSoftwareAsync(int releaseId)
{
try
{
List<SoftwareBySoftwareReleaseDto> software =
await client.Software.Releases[releaseId].Software.GetAsync();
return software ?? [];
}
catch
{
return [];
}
}
public async Task<(bool succeeded, string error)> AddIncludedSoftwareAsync(SoftwareBySoftwareReleaseDto dto)
{
try
{
await client.Software.Releases[(int)(dto.ReleaseId ?? 0)].Software.PostAsync(dto);
return (true, null);
}
catch(ApiException ex)
{
return (false, ExtractDetail(ex));
}
catch(Exception ex)
{
return (false, ex.Message);
}
}
public async Task<(bool succeeded, string error)> RemoveIncludedSoftwareAsync(int releaseId, int softwareId)
{
try
{
await client.Software.Releases[releaseId].Software[softwareId].DeleteAsync();
return (true, null);
}
catch(ApiException ex)
{
return (false, ExtractDetail(ex));
}
catch(Exception ex)
{
return (false, ex.Message);
}
}
public async Task<List<SoftwareDto>> GetAllSoftwareForPickerAsync()
{
try

View File

@@ -1721,21 +1721,6 @@ public class SoftwareService(Marechai.ApiClient.Client client, IRequestAdapter r
}
}
public async Task<List<SoftwareVersionBySoftwareReleaseDto>> GetIncludedVersionsAsync(int releaseId)
{
try
{
List<SoftwareVersionBySoftwareReleaseDto> versions =
await client.Software.Releases[releaseId].Versions.GetAsync();
return versions ?? [];
}
catch
{
return [];
}
}
public async Task<List<SoftwareReleaseDto>> GetCompilationsForSoftwareAsync(int softwareId)
{
try
@@ -1764,21 +1749,6 @@ public class SoftwareService(Marechai.ApiClient.Client client, IRequestAdapter r
}
}
public async Task<List<SoftwareBySoftwareReleaseDto>> GetIncludedSoftwareAsync(int releaseId)
{
try
{
List<SoftwareBySoftwareReleaseDto> software =
await client.Software.Releases[releaseId].Software.GetAsync();
return software ?? [];
}
catch
{
return [];
}
}
// ── Description methods ──
public async Task<string> GetDescriptionTextAsync(int id, string lang = "eng")

View File

@@ -76,6 +76,7 @@
<MudNavLink Href="/admin/software-families" Icon="@Icons.Material.Filled.FolderSpecial">@L["Software Family Management"]</MudNavLink>
<MudNavLink Href="/admin/software-platforms" Icon="@Icons.Material.Filled.Devices">@L["Software Platform Management"]</MudNavLink>
<MudNavLink Href="/admin/software/releases" Icon="@Icons.Material.Filled.Album">@L["Software Release Management"]</MudNavLink>
<MudNavLink Href="/admin/software-compilations" Icon="@Icons.Material.Filled.Collections">@L["Software Compilation Management"]</MudNavLink>
<MudNavLink Href="/admin/software-attributes" Icon="@Icons.Material.Filled.Tune">@L["Software Attributes Management"]</MudNavLink>
</MudNavMenu>
</Authorized>

74489
openapi.yaml Normal file

File diff suppressed because it is too large Load Diff