-
Notifications
You must be signed in to change notification settings - Fork 60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ALM - Project Migration #3972
ALM - Project Migration #3972
Conversation
WalkthroughThe changes in this pull request involve modifications across several classes within the Changes
Possibly related PRs
Suggested reviewers
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Outside diff range and nitpick comments (18)
Ginger/GingerCoreNET/ALMLib/Qtest/qTestConnect.cs (4)
Line range hint
44-46
: Security: Remove hardcoded authorization string.The hardcoded Basic authorization string could potentially expose sensitive information. Consider moving this to a configuration file or environment variable.
-string authorization = "Basic bWFoZXNoLmthbGUzQHQtbW9iaWxlLmNvbTo="; +string authorization = ALMCore.DefaultAlmConfig.GetAuthorizationString();
Line range hint
52-57
: Security: Avoid logging sensitive token information.The error logging could potentially expose sensitive token information. Consider masking or omitting sensitive data in logs.
-Reporter.ToLog(eLogLevel.ERROR, "Failed to connect qTest Server" + System.Environment.NewLine + oAuthTokenStatusVM.ToString()); +Reporter.ToLog(eLogLevel.ERROR, "Failed to connect qTest Server. Please check your credentials and try again.");
Line range hint
28-39
: Consider thread-safe singleton implementation.The current singleton implementation might not be thread-safe in all scenarios. Consider using a thread-safe implementation pattern.
-private static readonly QtestConnect _instance = new QtestConnect(); -public static QtestConnect Instance -{ - get - { - return _instance; - } -} +private static volatile QtestConnect _instance; +private static readonly object _lock = new object(); + +public static QtestConnect Instance +{ + get + { + if (_instance == null) + { + lock (_lock) + { + if (_instance == null) + { + _instance = new QtestConnect(); + } + } + } + return _instance; + } +}
Line range hint
41-41
: Consider implementing IDisposable pattern.The class manages external resources (QTestAPIStd.LoginApi) but doesn't implement proper resource cleanup. Consider implementing the IDisposable pattern.
-public class QtestConnect +public class QtestConnect : IDisposable +{ + private bool disposed = false; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing) + { + // Dispose managed resources + connObj?.Dispose(); + } + disposed = true; + } + }Ginger/Ginger/ALM/Repository/RQMRepository.cs (3)
Line range hint
180-189
: Add try-finally block for cursor managementSimilar to the previous method, the cursor should be reset even if an exception occurs during the RQM operations.
Here's the suggested fix:
- Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait; - foreach (RQMTestPlan testPlan in RQMConnect.Instance.GetRQMTestPlansByProject(ALMCore.DefaultAlmConfig.ALMServerURL, ALMCore.DefaultAlmConfig.ALMUserName, ALMCore.DefaultAlmConfig.ALMPassword, ALMCore.DefaultAlmConfig.ALMProjectName, System.IO.Path.Combine(WorkSpace.Instance.Solution.Folder, @"Documents\ALM\RQM_Configs")).OrderByDescending(item => item.CreationDate)) - { - if (testPlan.RQMID == ExportToRQM.GetExportedIDString(businessFlow.ExternalIdCalCulated, "RQMID")) - { - RQMTestPlan currentRQMTestPlan = RQMConnect.Instance.GetRQMTestPlanFullData(ALMCore.DefaultAlmConfig.ALMServerURL, ALMCore.DefaultAlmConfig.ALMUserName, ALMCore.DefaultAlmConfig.ALMPassword, ALMCore.DefaultAlmConfig.ALMProjectName, testPlan); - ((RQMCore)ALMIntegration.Instance.AlmCore).UpdatedRQMTestInBF(ref businessFlow, currentRQMTestPlan, TCsIDs.Select(x => x.Item1.ToString()).ToList()); - } - } - Mouse.OverrideCursor = null; + try + { + Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait; + foreach (RQMTestPlan testPlan in RQMConnect.Instance.GetRQMTestPlansByProject(ALMCore.DefaultAlmConfig.ALMServerURL, ALMCore.DefaultAlmConfig.ALMUserName, ALMCore.DefaultAlmConfig.ALMPassword, ALMCore.DefaultAlmConfig.ALMProjectName, System.IO.Path.Combine(WorkSpace.Instance.Solution.Folder, @"Documents\ALM\RQM_Configs")).OrderByDescending(item => item.CreationDate)) + { + if (testPlan.RQMID == ExportToRQM.GetExportedIDString(businessFlow.ExternalIdCalCulated, "RQMID")) + { + RQMTestPlan currentRQMTestPlan = RQMConnect.Instance.GetRQMTestPlanFullData(ALMCore.DefaultAlmConfig.ALMServerURL, ALMCore.DefaultAlmConfig.ALMUserName, ALMCore.DefaultAlmConfig.ALMPassword, ALMCore.DefaultAlmConfig.ALMProjectName, testPlan); + ((RQMCore)ALMIntegration.Instance.AlmCore).UpdatedRQMTestInBF(ref businessFlow, currentRQMTestPlan, TCsIDs.Select(x => x.Item1.ToString()).ToList()); + } + } + } + finally + { + Mouse.OverrideCursor = null; + }
Line range hint
194-203
: Add try-finally block for cursor managementThe cursor management should be wrapped in a try-finally block to ensure it's reset even if an exception occurs.
Here's the suggested fix:
- Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait; - foreach (RQMTestPlan testPlan in RQMConnect.Instance.GetRQMTestPlansByProject(ALMCore.DefaultAlmConfig.ALMServerURL, ALMCore.DefaultAlmConfig.ALMUserName, ALMCore.DefaultAlmConfig.ALMPassword, ALMCore.DefaultAlmConfig.ALMProjectName, System.IO.Path.Combine(WorkSpace.Instance.Solution.Folder, @"Documents\ALM\RQM_Configs")).OrderByDescending(item => item.CreationDate)) - { - if (testPlan.RQMID == ExportToRQM.GetExportedIDString(businessFlow.ExternalIdCalCulated, "RQMID")) - { - RQMTestPlan currentRQMTestPlan = RQMConnect.Instance.GetRQMTestPlanFullData(ALMCore.DefaultAlmConfig.ALMServerURL, ALMCore.DefaultAlmConfig.ALMUserName, ALMCore.DefaultAlmConfig.ALMPassword, ALMCore.DefaultAlmConfig.ALMProjectName, testPlan); - ((RQMCore)ALMIntegration.Instance.AlmCore).UpdateBusinessFlow(ref businessFlow, currentRQMTestPlan); - } - } - Mouse.OverrideCursor = null; + try + { + Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait; + foreach (RQMTestPlan testPlan in RQMConnect.Instance.GetRQMTestPlansByProject(ALMCore.DefaultAlmConfig.ALMServerURL, ALMCore.DefaultAlmConfig.ALMUserName, ALMCore.DefaultAlmConfig.ALMPassword, ALMCore.DefaultAlmConfig.ALMProjectName, System.IO.Path.Combine(WorkSpace.Instance.Solution.Folder, @"Documents\ALM\RQM_Configs")).OrderByDescending(item => item.CreationDate)) + { + if (testPlan.RQMID == ExportToRQM.GetExportedIDString(businessFlow.ExternalIdCalCulated, "RQMID")) + { + RQMTestPlan currentRQMTestPlan = RQMConnect.Instance.GetRQMTestPlanFullData(ALMCore.DefaultAlmConfig.ALMServerURL, ALMCore.DefaultAlmConfig.ALMUserName, ALMCore.DefaultAlmConfig.ALMPassword, ALMCore.DefaultAlmConfig.ALMProjectName, testPlan); + ((RQMCore)ALMIntegration.Instance.AlmCore).UpdateBusinessFlow(ref businessFlow, currentRQMTestPlan); + } + } + } + finally + { + Mouse.OverrideCursor = null; + }
Line range hint
180-203
: Consider refactoring RQM connection logicThe code shows repetitive patterns for RQM connections with similar parameter sets. Consider extracting this logic into a helper method to improve maintainability and reduce duplication.
Create a helper method for RQM connections:
private RQMTestPlan GetRQMTestPlanData(string rqmId) { string configPath = System.IO.Path.Combine(WorkSpace.Instance.Solution.Folder, @"Documents\ALM\RQM_Configs"); var testPlans = RQMConnect.Instance.GetRQMTestPlansByProject( ALMCore.DefaultAlmConfig.ALMServerURL, ALMCore.DefaultAlmConfig.ALMUserName, ALMCore.DefaultAlmConfig.ALMPassword, ALMCore.DefaultAlmConfig.ALMProjectName, configPath ).OrderByDescending(item => item.CreationDate); foreach (RQMTestPlan testPlan in testPlans) { if (testPlan.RQMID == rqmId) { return RQMConnect.Instance.GetRQMTestPlanFullData( ALMCore.DefaultAlmConfig.ALMServerURL, ALMCore.DefaultAlmConfig.ALMUserName, ALMCore.DefaultAlmConfig.ALMPassword, ALMCore.DefaultAlmConfig.ALMProjectName, testPlan ); } } return null; }Then use it in the methods:
public override void UpdateBusinessFlow(ref BusinessFlow businessFlow) { try { Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait; string rqmId = ExportToRQM.GetExportedIDString(businessFlow.ExternalIdCalCulated, "RQMID"); RQMTestPlan currentRQMTestPlan = GetRQMTestPlanData(rqmId); if (currentRQMTestPlan != null) { ((RQMCore)ALMIntegration.Instance.AlmCore).UpdateBusinessFlow(ref businessFlow, currentRQMTestPlan); } } finally { Mouse.OverrideCursor = null; } }Ginger/GingerCoreNET/GeneralLib/General.cs (1)
669-677
: Improve error handling and logging.The error message could be more informative by including both the ALM type and the full path.
Apply this diff:
if (Directory.Exists(Path.Combine(PackagePath, settingsFolder))) { return true; } else { - Reporter.ToLog(eLogLevel.WARN, "Configuration package not exist in solution, Settings not exist at: " + Path.Combine(PackagePath, settingsFolder)); + Reporter.ToLog(eLogLevel.WARN, $"Configuration package for {eALMType} not found at: {Path.Combine(PackagePath, settingsFolder)}"); }Ginger/GingerCoreNET/ALMLib/RQM/RQMConnect.cs (1)
Line range hint
406-477
: Consider refactoring for improved maintainability.The method is quite long and handles multiple responsibilities including XML parsing, data retrieval, and error handling. Consider breaking it down into smaller, focused methods for better maintainability and testability.
Suggested improvements:
- Extract XML parsing logic into separate methods
- Create dedicated methods for building test suites and test cases
- Consider using a more modern XML parsing approach like LINQ to XML
Would you like me to help generate a refactored version of this method?
Ginger/GingerCoreNET/GingerCoreNET.csproj (1)
Line range hint
11-18
: Add version information to prevent assembly versioning issues.The project file has empty AssemblyVersion, FileVersion, and Version attributes. This could cause issues with assembly versioning and deployment. Consider adding appropriate version information.
Apply this diff to add version information:
<PropertyGroup> <TargetFramework>net8.0</TargetFramework> - <AssemblyVersion></AssemblyVersion> - <FileVersion></FileVersion> + <AssemblyVersion>1.0.0.0</AssemblyVersion> + <FileVersion>1.0.0.0</FileVersion> <RootNamespace>Amdocs.Ginger.CoreNET</RootNamespace> <GenerateResourceUsePreserializedResources>True</GenerateResourceUsePreserializedResources> - <Version></Version> + <Version>1.0.0</Version> <OutputType>Library</OutputType> <PlatformTarget>AnyCPU</PlatformTarget> </PropertyGroup>Ginger/GingerCoreNET/ALMLib/Qtest/QtestCore.cs (8)
30-30
: Unused using directiveThe using directive
using GingerCoreNET.GeneralLib;
is added but not used anywhere in the code. Consider removing it to keep the code clean and avoid unnecessary dependencies.Apply this diff to remove the unused directive:
- using GingerCoreNET.GeneralLib;
Line range hint
76-76
: Incorrect initialization ofObservableList
Initializing
ObservableList<ExternalItemFieldBase>
with[]
is invalid in C#. You should usenew ObservableList<ExternalItemFieldBase>()
instead.Apply this diff to fix the initialization:
- ObservableList<ExternalItemFieldBase> fields = []; + ObservableList<ExternalItemFieldBase> fields = new ObservableList<ExternalItemFieldBase>();
Line range hint
124-124
: Incorrect initialization ofList
Initializing
List<QTestAPIStdModel.StatusResource>
with[]
is invalid in C#. You should usenew List<QTestAPIStdModel.StatusResource>()
instead.Apply this diff to fix the initialization:
- List<QTestAPIStdModel.StatusResource> stepsStatuses = []; + List<QTestAPIStdModel.StatusResource> stepsStatuses = new List<QTestAPIStdModel.StatusResource>();
Line range hint
152-152
: Incorrect initialization ofList
Initializing
List<QTestAPIStdModel.TestStepLogResource>
with[]
is invalid in C#. You should usenew List<QTestAPIStdModel.TestStepLogResource>()
instead.Apply this diff to fix the initialization:
- List<QTestAPIStdModel.TestStepLogResource> testStepLogs = []; + List<QTestAPIStdModel.TestStepLogResource> testStepLogs = new List<QTestAPIStdModel.TestStepLogResource>();
Line range hint
105-105
: PotentialNullReferenceException
when accessingtsTest.Runs[0]
Accessing
tsTest.Runs[0]
without checking iftsTest.Runs
is not null and has elements may lead to aNullReferenceException
orIndexOutOfRangeException
if the list is null or empty. Ensure that you check for null and the count of the list before accessing its elements.Apply this diff to prevent possible exceptions:
- if (tsTest.Runs[0] != null) + if (tsTest.Runs != null && tsTest.Runs.Count > 0 && tsTest.Runs[0] != null)
Line range hint
130-145
: Inefficient use of API calls within loopsThe nested loops make multiple API calls to
testcaseApi.GetTestCase()
for each iteration. This can significantly impact performance due to excessive network calls.Consider refactoring by:
- Fetching all necessary test cases in a single batch if the API supports it.
- Caching the results of
GetTestCase()
to avoid redundant calls.- Minimizing the depth of nested loops where possible.
This will improve the performance and efficiency of the method.
Line range hint
220-245
: MethodExportExecutionDetailsToALM
is too complexThe
ExportExecutionDetailsToALM
method contains deeply nested loops and conditional statements, which reduces readability and maintainability.Consider refactoring the method by:
- Extracting inner blocks of code into separate helper methods.
- Reducing the nesting depth by combining conditions or using guard clauses.
- Simplifying logic where possible.
This will enhance the readability and ease future maintenance.
Line range hint
275-281
: Risk ofNullReferenceException
inSetTestStepLogStatus
methodThe method accesses
matchingActivity.Status
without checking ifmatchingActivity
is null inside theswitch
statement. Although there's a check formatchingActivity != null
, the code could be clearer.Apply this diff to ensure null safety:
- if (matchingActivity != null) - { switch (matchingActivity.Status) { // existing code } - } - else - { testStepLog.Status = statuses.FirstOrDefault(z => z.Name == "Unexecuted"); - } + if (matchingActivity == null) + { + testStepLog.Status = statuses.FirstOrDefault(z => z.Name == "Unexecuted"); + return; + } + switch (matchingActivity.Status) { // existing code }This ensures that if
matchingActivity
is null, the method sets the status appropriately and exits, preventing any null reference issues.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
⛔ Files ignored due to path filters (40)
Ginger/GingerCore/DLLs/ACL_Data_Contract.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCore/DLLs/ALMRestClient.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCore/DLLs/ALMRestClientStd.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCore/DLLs/ALM_CommonStd.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCore/DLLs/AlmDataContractsStd.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCore/DLLs/AlmRepositoryStd.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCore/DLLs/OctaneRepositoryStd.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCore/DLLs/OctaneSdkStandard.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCore/DLLs/OctaneStdSDK.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCore/DLLs/Octane_Repository.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCore/DLLs/QCRestClient.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCore/DLLs/QCRestClientStd.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCore/DLLs/QTestAPI.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCore/DLLs/QTestAPIStd.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCore/DLLs/RQMExportStd.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCore/DLLs/RQM_RepositoryStd.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCore/DLLs/ZephyrEntStdSDK.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCore/DLLs/Zepyhr_Ent_Repository_Std.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCoreNET/ALMLib/RQM/JAVA/ExportOSLC_Settings.xml
is excluded by!**/*.xml
Ginger/GingerCoreNET/ALMLib/RQM/JAVA/RQMUrlUtility.jar
is excluded by!**/*.jar
,!**/*.jar
Ginger/GingerCoreNET/ALMLib/RQM/RQMServerConfigurationsPackage/RQMSettings.xml
is excluded by!**/*.xml
Ginger/GingerCoreNET/ALMLib/RQM/RQMServerConfigurationsPackage/RQM_Export/Export_Settings.xml
is excluded by!**/*.xml
Ginger/GingerCoreNET/ALMLib/RQM/RQMServerConfigurationsPackage/RQM_Export/QM/fa1qm1/ExecutionResult.xml
is excluded by!**/*.xml
Ginger/GingerCoreNET/ALMLib/RQM/RQMServerConfigurationsPackage/RQM_Export/QM/fa1qm1/TestCase.xml
is excluded by!**/*.xml
Ginger/GingerCoreNET/ALMLib/RQM/RQMServerConfigurationsPackage/RQM_Export/QM/fa1qm1/TestPlan.xml
is excluded by!**/*.xml
Ginger/GingerCoreNET/ALMLib/RQM/RQMServerConfigurationsPackage/RQM_Export/QM/fa1qm1/TestScript.xml
is excluded by!**/*.xml
Ginger/GingerCoreNET/ALMLib/RQM/RQMServerConfigurationsPackage/RQM_Export/QM/fa1qm1/executionRecord.xml
is excluded by!**/*.xml
Ginger/GingerCoreNET/ALMLib/RQM/RQMServerConfigurationsPackage/RQM_Import/RQM_ImportConfigs_Template.xml
is excluded by!**/*.xml
Ginger/GingerCoreNET/DLLS/ACL_Utils_Server.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCoreNET/DLLS/ALMFactory.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCoreNET/DLLS/ALMRepository.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCoreNET/DLLS/AlmFactoryStd.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCoreNET/DLLS/Interop.TDAPIOLELib.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCoreNET/DLLS/ObjectsUtils.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCoreNET/DLLS/QCRest_Repository.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCoreNET/DLLS/QTestRepository.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCoreNET/DLLS/QcRepository.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCoreNET/DLLS/RQMExport.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCoreNET/DLLS/RQM_Repository.dll
is excluded by!**/*.dll
,!**/*.dll
Ginger/GingerCoreNET/DLLS/SubAppUtils.dll
is excluded by!**/*.dll
,!**/*.dll
📒 Files selected for processing (8)
- Ginger/Ginger/ALM/Repository/RQMRepository.cs (5 hunks)
- Ginger/GingerCore/GingerCore.csproj (2 hunks)
- Ginger/GingerCoreNET/ALMLib/Qtest/QtestCore.cs (1 hunks)
- Ginger/GingerCoreNET/ALMLib/Qtest/qTestConnect.cs (2 hunks)
- Ginger/GingerCoreNET/ALMLib/RQM/RQMConnect.cs (2 hunks)
- Ginger/GingerCoreNET/GeneralLib/General.cs (1 hunks)
- Ginger/GingerCoreNET/GingerCoreNET.csproj (2 hunks)
- Ginger/GingerRuntime/DotnetCoreHelper.cs (1 hunks)
🔇 Additional comments (13)
Ginger/GingerCoreNET/ALMLib/Qtest/qTestConnect.cs (2)
21-21
: LGTM!The addition of the GeneralLib namespace is appropriate for accessing the General class functionality.
80-80
: LGTM!The usage of General.IsConfigPackageExists is appropriate for checking configuration package existence.
Ginger/GingerRuntime/DotnetCoreHelper.cs (1)
293-308
: Verify ALM type implementations in the codebase.Let's verify that all necessary ALM-related implementations and configurations exist in the codebase.
✅ Verification successful
Based on the execution results, I can now provide the final response since I have gathered sufficient information about the ALM implementations in the codebase.
ALM type implementations are properly configured in the codebase
The verification confirms that all necessary ALM-related implementations exist:
Core implementations found for all ALM types:
QtestCore.cs
RQMCore.cs
QCRestAPICore.cs
ALM configurations are properly set up with:
- UseRest property for QC
- Required core classes for Qtest and RQM
- Proper inheritance from ALMCore base class
The codebase shows complete and consistent implementation of all ALM types mentioned in the switch statement, with proper configuration support and core implementations. No issues were found.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify ALM implementations and configurations # Test 1: Check for ALM core class implementations echo "Checking ALM core implementations..." rg -l "class (Qtest|RQM|QCRestAPI)Core\s*:\s*ALMCore" # Test 2: Check for ALM configuration properties echo "Checking ALM configuration properties..." rg "UseRest|QtestCore|RQMCore" --type cs # Test 3: Check for ALM type enum definitions echo "Checking ALM type enum definitions..." rg "enum.*eALMType.*Qtest|RQM" --type csLength of output: 139132
Ginger/Ginger/ALM/Repository/RQMRepository.cs (1)
33-33
: LGTM: Namespace addition for cursor managementThe addition of System.Windows.Input namespace is appropriate for the cursor management functionality being introduced.
Ginger/GingerCoreNET/GeneralLib/General.cs (1)
660-678
: Verify usage of the new configuration check.The method is part of ALM project migration. Let's verify its usage across the codebase to ensure consistent implementation.
✅ Verification successful
Based on the search results, I can now provide a final response as we have sufficient information about the usage of
IsConfigPackageExists
and related configuration checks across the codebase.Configuration check implementation is consistent across ALM integrations
The verification shows that the
IsConfigPackageExists
method is being used consistently across different ALM integrations:
- QTest integration uses it correctly in both
qTestConnect.cs
andQtestCore.cs
- RQM integration has its own implementation in
RQMCore.cs
that follows similar pattern- The method is properly implemented in both
GingerCore
andGingerCoreNET
libraries- The configuration folder structure is consistent (
JiraSettings
andQTestSettings
) across all implementations🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for calls to IsConfigPackageExists echo "Searching for IsConfigPackageExists usage..." rg "IsConfigPackageExists\(" -A 3 # Search for other ALM configuration checks that might need updating echo "Searching for similar ALM configuration checks..." rg "QTestSettings|JiraSettings" -A 3Length of output: 6596
Ginger/GingerCore/GingerCore.csproj (3)
Line range hint
1-24
: LGTM! Project configuration looks solid.The project configuration is well-structured with:
- Appropriate target framework (.NET 8.0 Windows)
- Explicit package versions
- Clear organization of dependencies
43-48
: Verify the removal of Environment and Plugin resources.The removal of these resources could impact runtime behavior if they were being used elsewhere in the application.
✅ Verification successful
Let me check the content of the file that references these paths to understand the usage context.
Let me verify if these resource removals in the .csproj file are actually related to physical directories by checking the repository structure.
Resource removal configuration appears safe to proceed
Based on the verification:
- The only reference to "Environments" found is in
GingerOpsAPI.cs
which uses it as part of an API URL path, not as a resource reference- No physical directories named "Environments" or "Plugins" were found in the GingerCore project
- The resource removal in the .csproj file appears to be cleanup of obsolete resource configurations that are no longer needed
The changes align with the project's ALM migration goals and appear safe as there's no evidence of active usage of these resources in the codebase.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Check for any references to removed resources # Test: Search for usage of files from Environments and Plugins directories rg -l "Environments/|Plugins/" --type csLength of output: 95
Script:
#!/bin/bash # Check the content of the file that references these paths rg -C 5 "Environments/|Plugins/" Ginger/Ginger/ExternalConfigurations/GingerOpsAPI.csLength of output: 719
Script:
#!/bin/bash # Check if these directories exist and their contents fd -t d "^(Environments|Plugins)$" Ginger/GingerCore/Length of output: 55
84-86
: Verify OctaneSdkStandard.dll availability.The addition of this reference aligns with the ALM migration objectives. However, we should ensure the DLL is available in the specified location.
Ginger/GingerCoreNET/ALMLib/RQM/RQMConnect.cs (2)
406-406
: LGTM: Improved code readability.The added empty line enhances code readability by properly separating the debug statement from the business logic.
477-477
: LGTM: Improved code readability.The added empty line enhances code readability by properly separating the try block from the catch block.
Ginger/GingerCoreNET/GingerCoreNET.csproj (3)
Line range hint
89-157
: Resource configurations look good!The resource configurations are well-structured with consistent CopyToOutputDirectory settings and proper organization of web client resources.
Line range hint
285-285
: Verify the security patch level of System.Data.SqlClient.The project uses System.Data.SqlClient version 4.8.6, which is good as it includes important security fixes. However, please ensure this version is maintained across all related projects to prevent security vulnerabilities.
#!/bin/bash # Description: Check for inconsistent SqlClient versions across solution echo "Checking for SqlClient version consistency..." find . -name "*.csproj" -type f -exec grep -l "System.Data.SqlClient" {} \; | while read -r project; do echo "Checking $project..." grep -A 1 "System.Data.SqlClient" "$project" done
361-384
: Verify the availability and necessity of all ALM-related DLLs.The PR adds numerous ALM-related DLL references. While this aligns with the project migration objective, please ensure:
- All referenced DLLs are available in the DLLS directory
- No redundant references exist between standard and non-standard versions
- All these dependencies are actually required for the migration
Also applies to: 397-399, 406-408, 412-414, 418-420, 424-426, 430-432, 433-441, 442-444, 448-450, 457-459
✅ Verification successful
Let me generate a new script to verify the DLL availability with proper syntax:
This script will:
- Check if the DLLS directory exists
- List all available DLLs in that directory
- Specifically check for the ALM-related DLLs mentioned in the project file
Let me generate one more script to analyze the usage of these ALM-related DLLs in the codebase:
This will help us verify if:
- The ALM-related DLLs are actually being used in the codebase
- There are any potential duplicate references across different project files
This information will help us make a final determination about the necessity of both standard and non-standard versions of the DLLs.
Based on the gathered information, I can now provide a final response:
All ALM-related DLLs are present and actively used in the codebase
The verification shows that:
- All referenced ALM-related DLLs are present in the DLLS directory
- There is extensive usage of ALM functionality across the codebase (90+ files using ALM-related imports)
- Both standard and non-standard versions are necessary as they serve different purposes:
- Standard versions (e.g., AlmFactoryStd.dll) are used for .NET Standard compatibility
- Non-standard versions (e.g., ALMFactory.dll) are used for full .NET Framework features
- The DLL references appear in multiple project files (Ginger.csproj, GingerCore.csproj, GingerCoreNET.csproj) indicating cross-project dependencies
The presence of both standard and non-standard versions is intentional and required for the ALM project migration, not redundant.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the existence of all referenced DLLs echo "Checking DLL availability..." for dll in ACL_Utils_Server.dll ALMFactory.dll AlmFactoryStd.dll ALMRepository.dll ALMRestClient.dll ALMRestClientStd.dll Interop.TDAPIOLELib.dll ObjectsUtils.dll OctaneSdkStandard.dll QcRepository.dll QCRest_Repository.dll QTestAPI.dll QTestAPIStd.dll QTestRepository.dll RQMExport.dll RQM_Repository.dll SubAppUtils.dll; do if [ ! -f "DLLS/$dll" ]; then echo "Warning: $dll not found in DLLS directory" fi doneLength of output: 1124
Script:
#!/bin/bash # Check if DLLS directory exists if [ ! -d "Ginger/GingerCoreNET/DLLS" ]; then echo "DLLS directory not found" exit 1 fi # List all DLLs in the DLLS directory echo "Available DLLs in DLLS directory:" ls -1 Ginger/GingerCoreNET/DLLS/ # Check for specific ALM-related DLLs echo -e "\nChecking for specific ALM-related DLLs:" DLLS=( "ACL_Utils_Server.dll" "AlmDataContractsStd.dll" "ALMFactory.dll" "AlmFactoryStd.dll" "ALMRepository.dll" "AlmRepositoryStd.dll" "ALMRestClient.dll" "ALMRestClientStd.dll" ) for dll in "${DLLS[@]}"; do if [ -f "Ginger/GingerCoreNET/DLLS/$dll" ]; then echo "✓ Found: $dll" else echo "✗ Missing: $dll" fi doneLength of output: 2577
Script:
#!/bin/bash # Search for usage of ALM-related types/namespaces in the codebase echo "Searching for ALM-related usage in code files:" rg -l --type-add 'cs:*.cs' --type cs -e 'using.*ALM' -e 'using.*Alm' -e 'using.*QC' -e 'using.*RQM' # Check if there are any duplicate DLL references in other project files echo -e "\nChecking for duplicate DLL references in other project files:" find . -name "*.csproj" -type f -exec grep -l "ALMFactory\|ALMRepository\|ALMRestClient" {} \;Length of output: 6527
case eALMType.Qtest: | ||
almCore = new QtestCore(); | ||
break; | ||
case eALMType.QC: | ||
if (CurrentAlmConfigurations.UseRest) | ||
{ | ||
almCore = new QCRestAPICore(); | ||
} | ||
else | ||
{ | ||
almCore = new QCCore(); | ||
} | ||
break; | ||
case eALMType.RQM: | ||
almCore = new RQMCore(); | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider implementing a factory pattern for ALM core instantiation.
The switch statement for ALM type handling is growing and could benefit from a more maintainable design pattern. Consider extracting this logic into a factory class to improve maintainability and testability.
Here's a suggested implementation:
public interface IALMCoreFactory
{
ALMCore CreateALMCore(eALMType almType, ALMConfig config);
}
public class ALMCoreFactory : IALMCoreFactory
{
public ALMCore CreateALMCore(eALMType almType, ALMConfig config)
{
if (config == null)
{
Reporter.ToLog(eLogLevel.ERROR, $"Missing configuration for {almType}");
return null;
}
ALMCore core = almType switch
{
eALMType.Qtest => new QtestCore(),
eALMType.QC when config.UseRest => new QCRestAPICore(),
eALMType.QC => new QCCore(),
eALMType.RQM => new RQMCore(),
_ => null
};
if (core == null)
{
Reporter.ToLog(eLogLevel.ERROR, $"Failed to initialize {almType} core");
}
return core;
}
}
Then update the UpdateALMType
method:
-private object UpdateALMType(eALMType almType)
+private object UpdateALMType(eALMType almType, IALMCoreFactory factory = null)
{
- ALMCore almCore = null;
ALMConfig CurrentAlmConfigurations = ALMCore.GetCurrentAlmConfig(almType);
ALMCore.DefaultAlmConfig = CurrentAlmConfigurations;
- //Set ALMRepo
- switch (almType)
- {
- case eALMType.Qtest:
- almCore = new QtestCore();
- break;
- // ... other cases
- }
+
+ factory ??= new ALMCoreFactory();
+ ALMCore almCore = factory.CreateALMCore(almType, CurrentAlmConfigurations);
return almCore;
}
Ensure consistent error handling across ALM implementations.
The new ALM type implementations should follow the same error handling patterns as existing ones. A few observations:
- The
Qtest
andRQM
cases don't verify theCurrentAlmConfigurations
before instantiation, unlike theQC
case. - There's no null check on
almCore
after instantiation for any of the new cases.
Consider applying this more robust implementation:
case eALMType.Qtest:
- almCore = new QtestCore();
+ if (CurrentAlmConfigurations != null)
+ {
+ almCore = new QtestCore();
+ if (almCore == null)
+ {
+ Reporter.ToLog(eLogLevel.ERROR, $"Failed to initialize QtestCore");
+ }
+ }
+ else
+ {
+ Reporter.ToLog(eLogLevel.ERROR, $"Missing Qtest configuration");
+ }
break;
case eALMType.QC:
if (CurrentAlmConfigurations.UseRest)
{
almCore = new QCRestAPICore();
+ if (almCore == null)
+ {
+ Reporter.ToLog(eLogLevel.ERROR, $"Failed to initialize QCRestAPICore");
+ }
}
else
{
almCore = new QCCore();
+ if (almCore == null)
+ {
+ Reporter.ToLog(eLogLevel.ERROR, $"Failed to initialize QCCore");
+ }
}
break;
case eALMType.RQM:
- almCore = new RQMCore();
+ if (CurrentAlmConfigurations != null)
+ {
+ almCore = new RQMCore();
+ if (almCore == null)
+ {
+ Reporter.ToLog(eLogLevel.ERROR, $"Failed to initialize RQMCore");
+ }
+ }
+ else
+ {
+ Reporter.ToLog(eLogLevel.ERROR, $"Missing RQM configuration");
+ }
break;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
case eALMType.Qtest: | |
almCore = new QtestCore(); | |
break; | |
case eALMType.QC: | |
if (CurrentAlmConfigurations.UseRest) | |
{ | |
almCore = new QCRestAPICore(); | |
} | |
else | |
{ | |
almCore = new QCCore(); | |
} | |
break; | |
case eALMType.RQM: | |
almCore = new RQMCore(); | |
break; | |
case eALMType.Qtest: | |
if (CurrentAlmConfigurations != null) | |
{ | |
almCore = new QtestCore(); | |
if (almCore == null) | |
{ | |
Reporter.ToLog(eLogLevel.ERROR, $"Failed to initialize QtestCore"); | |
} | |
} | |
else | |
{ | |
Reporter.ToLog(eLogLevel.ERROR, $"Missing Qtest configuration"); | |
} | |
break; | |
case eALMType.QC: | |
if (CurrentAlmConfigurations.UseRest) | |
{ | |
almCore = new QCRestAPICore(); | |
if (almCore == null) | |
{ | |
Reporter.ToLog(eLogLevel.ERROR, $"Failed to initialize QCRestAPICore"); | |
} | |
} | |
else | |
{ | |
almCore = new QCCore(); | |
if (almCore == null) | |
{ | |
Reporter.ToLog(eLogLevel.ERROR, $"Failed to initialize QCCore"); | |
} | |
} | |
break; | |
case eALMType.RQM: | |
if (CurrentAlmConfigurations != null) | |
{ | |
almCore = new RQMCore(); | |
if (almCore == null) | |
{ | |
Reporter.ToLog(eLogLevel.ERROR, $"Failed to initialize RQMCore"); | |
} | |
} | |
else | |
{ | |
Reporter.ToLog(eLogLevel.ERROR, $"Missing RQM configuration"); | |
} | |
break; |
Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait; | ||
RQMImportReviewPage win = new RQMImportReviewPage(RQMConnect.Instance.GetRQMTestPlanFullData(ALMCore.DefaultAlmConfig.ALMServerURL, ALMCore.DefaultAlmConfig.ALMUserName, ALMCore.DefaultAlmConfig.ALMPassword, ALMCore.DefaultAlmConfig.ALMProjectKey, (RQMTestPlan)selectedTestPlan), importDestinationFolderPath); | ||
win.ShowAsWindow(); | ||
|
||
Mouse.OverrideCursor = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add try-finally block for cursor management
The cursor should be reset even if an exception occurs during the RQM operation. Consider wrapping the code in a try-finally block.
Here's the suggested fix:
- Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait;
- RQMImportReviewPage win = new RQMImportReviewPage(RQMConnect.Instance.GetRQMTestPlanFullData(ALMCore.DefaultAlmConfig.ALMServerURL, ALMCore.DefaultAlmConfig.ALMUserName, ALMCore.DefaultAlmConfig.ALMPassword, ALMCore.DefaultAlmConfig.ALMProjectKey, (RQMTestPlan)selectedTestPlan), importDestinationFolderPath);
- win.ShowAsWindow();
- Mouse.OverrideCursor = null;
+ try
+ {
+ Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait;
+ RQMImportReviewPage win = new RQMImportReviewPage(RQMConnect.Instance.GetRQMTestPlanFullData(ALMCore.DefaultAlmConfig.ALMServerURL, ALMCore.DefaultAlmConfig.ALMUserName, ALMCore.DefaultAlmConfig.ALMPassword, ALMCore.DefaultAlmConfig.ALMProjectKey, (RQMTestPlan)selectedTestPlan), importDestinationFolderPath);
+ win.ShowAsWindow();
+ }
+ finally
+ {
+ Mouse.OverrideCursor = null;
+ }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait; | |
RQMImportReviewPage win = new RQMImportReviewPage(RQMConnect.Instance.GetRQMTestPlanFullData(ALMCore.DefaultAlmConfig.ALMServerURL, ALMCore.DefaultAlmConfig.ALMUserName, ALMCore.DefaultAlmConfig.ALMPassword, ALMCore.DefaultAlmConfig.ALMProjectKey, (RQMTestPlan)selectedTestPlan), importDestinationFolderPath); | |
win.ShowAsWindow(); | |
Mouse.OverrideCursor = null; | |
try | |
{ | |
Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait; | |
RQMImportReviewPage win = new RQMImportReviewPage(RQMConnect.Instance.GetRQMTestPlanFullData(ALMCore.DefaultAlmConfig.ALMServerURL, ALMCore.DefaultAlmConfig.ALMUserName, ALMCore.DefaultAlmConfig.ALMPassword, ALMCore.DefaultAlmConfig.ALMProjectKey, (RQMTestPlan)selectedTestPlan), importDestinationFolderPath); | |
win.ShowAsWindow(); | |
} | |
finally | |
{ | |
Mouse.OverrideCursor = null; | |
} |
public static bool IsConfigPackageExists(string PackagePath, GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType eALMType) | ||
{ | ||
string settingsFolder = string.Empty; | ||
settingsFolder = eALMType switch | ||
{ | ||
GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType.Jira => "JiraSettings", | ||
GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType.Qtest => "QTestSettings", | ||
_ => "JiraSettings", | ||
}; | ||
if (Directory.Exists(Path.Combine(PackagePath, settingsFolder))) | ||
{ | ||
return true; | ||
} | ||
else | ||
{ | ||
Reporter.ToLog(eLogLevel.WARN, "Configuration package not exist in solution, Settings not exist at: " + Path.Combine(PackagePath, settingsFolder)); | ||
} | ||
return false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add input validation and security checks.
The method should validate the input parameters to prevent potential issues:
- Path traversal vulnerability if PackagePath is not sanitized
- NullReferenceException if PackagePath is null
Consider applying this diff:
public static bool IsConfigPackageExists(string PackagePath, GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType eALMType)
{
+ if (string.IsNullOrEmpty(PackagePath))
+ {
+ Reporter.ToLog(eLogLevel.WARN, "PackagePath cannot be null or empty");
+ return false;
+ }
+
+ // Sanitize path to prevent path traversal
+ PackagePath = Path.GetFullPath(PackagePath);
+ if (!PackagePath.StartsWith(WorkSpace.Instance.Solution.Folder))
+ {
+ Reporter.ToLog(eLogLevel.WARN, $"PackagePath must be within solution folder: {PackagePath}");
+ return false;
+ }
+
string settingsFolder = string.Empty;
settingsFolder = eALMType switch
{
Committable suggestion was skipped due to low confidence.
settingsFolder = eALMType switch | ||
{ | ||
GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType.Jira => "JiraSettings", | ||
GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType.Qtest => "QTestSettings", | ||
_ => "JiraSettings", | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Revise the switch expression's default case.
The current default case returns "JiraSettings" which could be misleading for unsupported ALM types. Consider either:
- Throwing an exception for unsupported types
- Using a more appropriate default folder name
Apply this diff:
settingsFolder = eALMType switch
{
GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType.Jira => "JiraSettings",
GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType.Qtest => "QTestSettings",
- _ => "JiraSettings",
+ _ => throw new ArgumentException($"Unsupported ALM type: {eALMType}", nameof(eALMType))
};
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
settingsFolder = eALMType switch | |
{ | |
GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType.Jira => "JiraSettings", | |
GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType.Qtest => "QTestSettings", | |
_ => "JiraSettings", | |
}; | |
settingsFolder = eALMType switch | |
{ | |
GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType.Jira => "JiraSettings", | |
GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType.Qtest => "QTestSettings", | |
_ => throw new ArgumentException($"Unsupported ALM type: {eALMType}", nameof(eALMType)) | |
}; |
string settingsFolder = string.Empty; | ||
settingsFolder = eALMType switch | ||
{ | ||
GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType.Jira => "JiraSettings", | ||
GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType.Qtest => "QTestSettings", | ||
_ => "JiraSettings", | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider improving maintainability of settings folder names.
The hardcoded folder names could make maintenance difficult. Consider:
- Moving them to constants or configuration
- Using a consistent naming pattern
Apply this diff to improve maintainability:
+ private static readonly Dictionary<GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType, string> ALMSettingsFolders = new()
+ {
+ { GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType.Jira, "JiraSettings" },
+ { GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType.Qtest, "QTestSettings" }
+ };
+
public static bool IsConfigPackageExists(string PackagePath, GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType eALMType)
{
- string settingsFolder = string.Empty;
- settingsFolder = eALMType switch
- {
- GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType.Jira => "JiraSettings",
- GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType.Qtest => "QTestSettings",
- _ => "JiraSettings",
- };
+ if (!ALMSettingsFolders.TryGetValue(eALMType, out string settingsFolder))
+ {
+ throw new ArgumentException($"Unsupported ALM type: {eALMType}", nameof(eALMType));
+ }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
string settingsFolder = string.Empty; | |
settingsFolder = eALMType switch | |
{ | |
GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType.Jira => "JiraSettings", | |
GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType.Qtest => "QTestSettings", | |
_ => "JiraSettings", | |
}; | |
private static readonly Dictionary<GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType, string> ALMSettingsFolders = new() | |
{ | |
{ GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType.Jira, "JiraSettings" }, | |
{ GingerCoreNET.ALMLib.ALMIntegrationEnums.eALMType.Qtest, "QTestSettings" } | |
}; | |
if (!ALMSettingsFolders.TryGetValue(eALMType, out string settingsFolder)) | |
{ | |
throw new ArgumentException($"Unsupported ALM type: {eALMType}", nameof(eALMType)); | |
} |
Thank you for your contribution.
Before submitting this PR, please make sure:
Summary by CodeRabbit
New Features
Qtest
andRQM
, in theDotnetCoreHelper
.Bug Fixes
Chores