XAPI Modules Update Guide¶
This guide explains how to update XAPI modules in the Virto Commerce Platform after upgrading GraphQL.NET libraries from v4 to v8. Follow these steps to update references, initialize modules, and handle breaking changes. Be sure to thoroughly test your implementations after making these updates.
Affected libraries and modules¶
GraphQL.NET libraries were updated from v4 to v8. The following modules are affected:
- VirtoCommerce.Xapi
- VirtoCommerce.XCatalog
- VirtoCommerce.XCart
- VirtoCommerce.XOrder
- VirtoCommerce.XCMS
- VirtoCommerce.ProfileExperienceApi
- VirtoCommerce.FileExperienceApi
- VirtoCommerce.MarketingExperienceApi
- VirtoCommerce.QuoteModule
- VirtoCommerce.PushMessages
- VirtoCommerce.TaskManagement
- VirtoCommerce.Skyflow
- VirtoCommerce.Contracts
- VirtoCommerce.CustomerReviews
- VirtoCommerce.Recommendations
- VirtoCommerce.WhiteLabeling
Use the PowerShell script below to update references in your projects.
PowerShell script for updating xAPI module references¶
This PowerShell script automates the process of updating XAPI module references in .csproj
files to specific new versions. Run this script before applying the code-level changes described in this guide.
The script scans all .csproj
files in the solution directory, identifies package references for specified XAPI modules, and updates them to the desired versions as defined in a hashtable.
Prerequisites¶
- Run the script in the root directory of the solution.
- Have the necessary permissions to modify
.csproj
files.
Scripts¶
# Define the hashtable with package partial names and new versions
$hash = @{
'VirtoCommerce.Xapi' = '3.901.0'
'VirtoCommerce.XCatalog' = '3.900.0'
'VirtoCommerce.XCart' = '3.900.0'
'VirtoCommerce.XOrder' = '3.900.0'
'VirtoCommerce.XCMS' = '3.900.0'
'VirtoCommerce.ProfileExperienceApi' = '3.900.0'
'VirtoCommerce.FileExperienceApi' = '3.900.0'
'VirtoCommerce.MarketingExperienceApi' = '3.900.0'
'VirtoCommerce.QuoteModule' = '3.900.0'
'VirtoCommerce.PushMessages' = '3.900.0'
'VirtoCommerce.TaskManagement' = '3.900.0'
'VirtoCommerce.Skyflow' = '3.900.0'
'VirtoCommerce.Contracts' = '3.900.0'
'VirtoCommerce.CustomerReviews' = '3.900.0'
'VirtoCommerce.WhiteLabeling' = '3.900.0'
'VirtoCommerce.XRecommend' = '3.900.0'
}
# Get all .csproj files in the solution root directory and subdirectories
$solutionRoot = Get-Location
$csprojFiles = Get-ChildItem -Path $solutionRoot -Recurse -Filter "*.csproj"
foreach ($csprojPath in $csprojFiles) {
Write-Host "Processing file: $csprojPath"
# Load the .csproj file into an XML document
[xml]$csprojXml = Get-Content -Path $csprojPath.FullName
# Namespace manager to handle XML namespaces (if any)
$namespaceManager = New-Object System.Xml.XmlNamespaceManager($csprojXml.NameTable)
$namespaceManager.AddNamespace("ns", $csprojXml.Project.NamespaceURI)
# Iterate through each package in the hashtable
foreach ($partialName in $hash.Keys) {
$newVersion = $hash[$partialName]
# Find PackageReference elements that match the partial name
$packageReferences = $csprojXml.SelectNodes("//ns:PackageReference[contains(@Include, '$partialName')]", $namespaceManager)
foreach ($packageReference in $packageReferences) {
# Update the Version attribute
$packageReference.Version = $newVersion
Write-Host "Updated $($packageReference.Include) to version $newVersion in file $csprojPath"
}
}
# Save the modified XML back to the .csproj file
$csprojXml.Save($csprojPath.FullName)
Write-Host "Updated .csproj file saved at $csprojPath"
}
Execution steps
- Copy the script to a
.ps1
file (e.g.,UpdateXapiModules.ps1
). - Open PowerShell in the root directory of your solution.
- Execute the script:
- Verify that
.csproj
files were updated successfully by checking the updated versions inPackageReference
entries.
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
// Define dictionary with package partial names and new versions
var packageVersions = new Dictionary<string, string>
{
{ "VirtoCommerce.Xapi", "3.901.0" },
{ "VirtoCommerce.XCatalog", "3.900.0" },
{ "VirtoCommerce.XCart", "3.900.0" },
{ "VirtoCommerce.XOrder", "3.900.0" },
{ "VirtoCommerce.XCMS", "3.900.0" },
{ "VirtoCommerce.ProfileExperienceApi", "3.900.0" },
{ "VirtoCommerce.FileExperienceApi", "3.900.0" },
{ "VirtoCommerce.MarketingExperienceApi", "3.900.0" },
{ "VirtoCommerce.QuoteModule", "3.900.0" },
{ "VirtoCommerce.PushMessages", "3.900.0" },
{ "VirtoCommerce.TaskManagement", "3.900.0" },
{ "VirtoCommerce.Skyflow", "3.900.0" },
{ "VirtoCommerce.Contracts", "3.900.0" },
{ "VirtoCommerce.CustomerReviews", "3.900.0" },
{ "VirtoCommerce.WhiteLabeling", "3.900.0" },
{ "VirtoCommerce.XRecommend", "3.900.0" }
};
// Get all .csproj files in the solution root directory and subdirectories
var solutionRoot = Directory.GetCurrentDirectory();
var csprojFiles = Directory.GetFiles(solutionRoot, "*.csproj", SearchOption.AllDirectories);
foreach (string csprojPath in csprojFiles)
{
Console.WriteLine($"Processing file: {csprojPath}");
// Load the .csproj file into an XML document
var csprojXml = new XmlDocument();
csprojXml.Load(csprojPath);
// Namespace manager to handle XML namespaces (if any)
var namespaceManager = new XmlNamespaceManager(csprojXml.NameTable);
if (csprojXml.DocumentElement?.NamespaceURI != null)
namespaceManager.AddNamespace("ns", csprojXml.DocumentElement.NamespaceURI);
// Iterate through each package in the dictionary
foreach (var package in packageVersions)
{
string partialName = package.Key;
string newVersion = package.Value;
// Find PackageReference elements that match the partial name
var packageReferences = csprojXml.SelectNodes($"//ns:PackageReference[contains(@Include, '{partialName}')]", namespaceManager);
if (packageReferences != null)
foreach (XmlNode packageReference in packageReferences)
{
// Update the Version attribute
packageReference.Attributes["Version"].Value = newVersion;
Console.WriteLine($"Updated {packageReference["Include"]} to version {newVersion} in file {csprojPath}");
}
}
// Save the modified XML back to the .csproj file
csprojXml.Save(csprojPath);
Console.WriteLine($"Updated .csproj file saved at {csprojPath}");
}
Execution Steps
- Install
dotnet-script
if not already installed:
- Copy the script to a
.csx
file (e.g.,UpdateXapiModules.csx
). - Open a terminal (Windows, macOS, or Linux) in the root directory of your solution.
- Execute the script:
- Verify that
.csproj
files were updated successfully by checking the updated versions inPackageReference
entries.
Now you can proceed with the code-level updates described.
Initialization changes¶
Single xAPI project example¶
// old
var assemblyMarker = typeof(AssemblyMarker);
var graphQlBuilder = new CustomGraphQLBuilder(serviceCollection);
graphQlBuilder.AddGraphTypes(assemblyMarker);
serviceCollection.AddMediatR(assemblyMarker);
serviceCollection.AddAutoMapper(assemblyMarker);
serviceCollection.AddSchemaBuilders(assemblyMarker);
// new
var graphQlBuilder = new GraphQLBuilder(serviceCollection, builder =>
{
builder.AddSchema(serviceCollection, typeof(AssemblyMarker));
});
Core/Data XAPI project example¶
// old
var graphQlBuilder = new CustomGraphQLBuilder(serviceCollection);
graphQLBuilder.AddSchema(typeof(CoreAssemblyMarker), typeof(DataAssemblyMarker));
// new
var graphQlBuilder = new GraphQLBuilder(serviceCollection, builder =>
{
builder.AddSchema(serviceCollection, typeof(CoreAssemblyMarker), typeof(DataAssemblyMarker));
});
Note
The new AddSchema
method will register GraphTypes, MediatR, AutoMapper, and Schema Builders.
Breaking changes¶
GraphTypeExtenstionHelper
was changed toGraphTypeExtensionHelper
.- The namespace
VirtoCommerce.XDigitalCatalog.Queries
was changed toVirtoCommerce.XCatalog.Core.Queries
. -
The async resolver
AsyncFieldResolver
was replaced withFuncFieldResolver
. Example:// old Resolver = new AsyncFieldResolver<object>(async context => { var result = await _mediator.Send(new GetCountriesQuery()); return result.Countries; }) // new Resolver = new FuncFieldResolver<object>(async context => { var result = await _mediator.Send(new GetCountriesQuery()); return result.Countries; })
-
The method
GraphTypeExtensionHelper.CreateConnection
now takes the name as the first parameter.ConnectionType.Name
extension method is obsolete. Example: -
The
FieldBuilder.Create
method now takes the name as the first parameter.FieldType.Name
extension method is obsolete. Example: -
The
EventStreamFieldType
was replaced withFieldType
, andIResolveEventStreamContext
was removed. Example:// old _ = new EventStreamFieldType { Name = "ping", Type = typeof(StringGraphType), Resolver = new FuncFieldResolver<string>(Resolve), AsyncSubscriber = new AsyncEventStreamResolver<string>(Subscribe), }; private async Task<IObservable<string>> Subscribe(IResolveEventStreamContext context) { //... // new _ = new FieldType { Name = "ping", Type = typeof(StringGraphType), Resolver = new FuncFieldResolver<string>(Resolve), StreamResolver = new SourceStreamResolver<string>(Subscribe), }; private async ValueTask<IObservable<string>> Subscribe(IResolveFieldContext context) { //...
Obsolete warnings¶
-
Field
methods that take a resolver or description as parameters are now marked as obsolete. Use extension methods instead. Example: -
The
FieldAsync
method was removed. Use theResolveAsync
extension method. Example:// old FieldAsync<StringGraphType>("outline", resolve: async context => { var response = await mediator.Send(loadRelatedCatalogOutlineQuery); return response.Outline; }); // new Field<StringGraphType>("outline") .ResolveAsync(async context => { var response = await mediator.Send(loadRelatedCatalogOutlineQuery); return response.Outline; });
-
Make sure
ExtendableField
methods do not accidentally call async functions without async/await. Example:// compiles but will raise an exception at runtime ExtendableField<NonNullGraphType<ListGraphType<NonNullGraphType<DynamicPropertyValueType>>>>( "dynamicProperties", context => dynamicPropertyResolverService.LoadDynamicPropertyValues(context.Source, context.GetCultureName())); // correct declaration ExtendableFieldAsync<NonNullGraphType<ListGraphType<NonNullGraphType<DynamicPropertyValueType>>>>( "dynamicProperties", async context => await dynamicPropertyResolverService.LoadDynamicPropertyValues(context.Source, context.GetCultureName()));
Settings updates¶
Some settings were moved to their corresponding X-modules and made public:
VirtoCommerce.Xapi.Core.ModuleConstants.Settings.General.IsSelectedForCheckout
—>VirtoCommerce.XCart.Core.ModuleConstants.Settings.General.IsSelectedForCheckout
VirtoCommerce.Xapi.Core.ModuleConstants.Settings.General.CreateAnonymousOrder
—>VirtoCommerce.XOrder.Core.ModuleConstants.Settings.General.CreateAnonymousOrder
Deprecated fields and mutations¶
Deprecated fields and mutations were removed to reduce redundancy and ensure schema clarity. Use the suggested alternatives:
Fields:¶
DynamicPropertyType.valueType
—> UsedynamicPropertyValueType
insteadInputDynamicPropertyValueType.locale
—> UsecultureName
fieldQuoteAttachmentType.mimeType
—> UsecontentType
fieldPriceType.validFrom
—> UsestartDate
fieldPriceType.validUntil
—> UseendDate
fieldPropertyType.type
—> UsepropertyType
fieldPropertyType.valueType
—> UsepropertyValueType
fieldPropertyType.propertyDictItems
—> UsepropertyDictionaryItems
field
Mutations:¶
renameWishlist
—> UsechangeWishlist
mutationprocessOrderPayment
—> UseinitializePayment
mutation