Skip to content

Fix broken command line options#392

Merged
gathogojr merged 3 commits intoOData:masterfrom
gathogojr:fix/390-fix-broken-command-line-options
May 13, 2024
Merged

Fix broken command line options#392
gathogojr merged 3 commits intoOData:masterfrom
gathogojr:fix/390-fix-broken-command-line-options

Conversation

@gathogojr
Copy link
Copy Markdown
Contributor

@gathogojr gathogojr commented May 6, 2024

This pull request fixes #390, fixes #391, fixes #384, and closes #389

This appears to have broken when introducing a feature that allows loading the command line options from a config file. Here's the pull request #363

It's a useful feature since it helps someone avoid having to specify the command line options all the time.

For the feature to work in a manner that makes logical sense, the value of a command line option that is explicitly specified should take precedence over the value loaded from the config file.

There was one challenge with this though...

Let's say the value for a particular option is true in the config file, how would we differentiate between false that is explicitly specified from the command line (such that it should take precedence over the config file true option) if an unspecified option also defaults to false?
This is the reason we changed the boolean options to be nullable such that their values would be null when not specified.

Users should still to be able to specify boolean options without an explicit true, e.g., --enable-tracking as opposed to --enable-tracking true. However, by making the options nullable, that capability broke. When a boolean option is not explicitly set to true or false, it's defaulting to null.

The workaround currently is to explicitly specify true or false, e.g., --enable-tracking true or --enable-tracking false.

I have since discovered that we could have achieved the objective without making the options nullable from the GenerateCommand class. We only need to make the property nullable from the GenerateOptions, i.e.:

public class GenerateCommand : Command
{
    public GenerateCommand()
        : base("generate", "...")
    {
        // ... other options
        Option enableTracking = new Option<bool>(new[] { "--enable-tracking", "-et" })
        {
            Name = "enable-tracking",
            Description = "Enable entity and property tracking."
        }
        // ... other options
    }
}

public class GenerateOptions
{
    // ... other properties
    public bool? EnableTracking { get; set; }
    // ... other properties
}

This resolves the issue. For example, with the above change, both --enable-tracking and --enable-tracking true initialize the EnableTracking property to true, while unspecified options default to null.

From my investigation, I also discovered that other users of the System.CommandLine library had made a feature request for command line options to support three states - undefined|null/true/false: commandlineparser/commandline#316
That feature has already been implemented and upgrading the CLI project to the latest version of the System.CommandLine library (without making any change) would also solve the issue.

This pull request resolves the reported issue by removing the ? operator from the options in GenerateCommand class. It also upgrades the System.CommandLine library to the latest version.

Additional work

  • Added tests for the CLI command options and code generation
  • Add support for ServiceName command line option to the CLI - This makes it possible to control the CSDL filename (Created as "Service NameCsdl.xml" by default)

@gathogojr
Copy link
Copy Markdown
Contributor Author

@gregwinterstein FYI

@gathogojr gathogojr force-pushed the fix/390-fix-broken-command-line-options branch 2 times, most recently from 5a97975 to 9714c48 Compare May 8, 2024 08:00
Comment thread ODataCodeGenTools.sln
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ODataConnectedService_VS2022Plus", "src\ODataConnectedService_VS2022Plus\ODataConnectedService_VS2022Plus.csproj", "{15769DAB-F3B0-4E82-A125-CF55ACB04D07}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.OData.Cli.Tests", "test\Microsoft.OData.Cli.Tests\Microsoft.OData.Cli.Tests.csproj", "{FE62623D-B8AF-49E2-AEB2-4EF50419777A}"
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes to this solution file are due to reorganizing the solution through introduction of src and test solution folders and moving different projects to their logical solution folder

Comment thread src/Microsoft.OData.Cli/GenerateCommand.cs Outdated
string namespacePrefix = null;
if (!(generateOptions.NamespacePrefix != null && generateOptions.NamespacePrefix.All(char.IsWhiteSpace)))
{
namespacePrefix = string.IsNullOrEmpty(generateOptions.NamespacePrefix) ? configUserSettings?.NamespacePrefix : generateOptions.NamespacePrefix;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we check string.IsNullOrEmpty if the wrapping if-block already checks that the string is null or all whitespace (which includes empty).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, does this mean that with your changes that the namespacePrefix = generateOptions.NamespacePrefix condition does not get reached anymore?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have the same question for all the similar changes to the code blocks below.

Copy link
Copy Markdown
Contributor Author

@gathogojr gathogojr May 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@habbes Like I explained offline, when either of namespace-prefix, excluded-operation-imports, excluded-bound-operations, and excluded-schema-types is explicitly specified from the command line as an empty string, that should interpreted as an indication that if there was a non-empty value specified previously undone, it should be undone. Otherwise, one would never be able to undo any namespace prefix, or excluded model elements from the config file.
As an illustration, consider a scenario, where on the first occasion I elected to have a namespace prefix "Client":

generate --metadata-uri http://tempuri.org --outputdir C:\tempdir --namespace-prefix Client

You end up with a config file with the following properties (among others):

{
    "Endpoint": "http://tempuri.org",
    "UseNamespacePrefix": true,
    "NamespacePrefix": "Client"
}

The logical way to unset the Client namespace prefix from the config file is as follows:

generate --metadata-uri http://tempuri.org --outputdir C:\tempdir --namespace-prefix "" --config-file C:\tempdir\ConnectedService.json

Where the empty string will be interpreted to mean I want to overwrite the value in the config file.

Same case with excluded model elements.
Consider a scenario, where on the first occasion I elected to exclude "ResetDataSource" operation import:

generate --metadata-uri http://tempuri.org --outputdir C:\tempdir --excluded-operation-imports ResetDataSource

You end up with a config file with the following properties (among others):

{
    "Endpoint": "http://tempuri.org",
    "ExcludedOperationImports": ["ResetDataSource"]
}

The logical way to unset the ResetDataSource excluded operation import from the config file is as follows:

generate --metadata-uri http://tempuri.org --outputdir C:\tempdir --excluded-operation-imports "" --config-file C:\tempdir\ConnectedService.json

Comment thread src/Microsoft.OData.Cli/GenerateCommand.cs Outdated
Comment thread src/Microsoft.OData.Cli/GenerateCommand.cs Outdated
Comment thread src/Microsoft.OData.Cli/GenerateCommand.cs Outdated
Comment thread test/Microsoft.OData.Cli.Tests/CodeGeneration/ODataCliCodeGenerationTests.cs Outdated
Comment thread test/Microsoft.OData.Cli.Tests/CodeGeneration/ODataCliCodeGenerationTests.cs Outdated
Comment thread test/Microsoft.OData.Cli.Tests/CodeGeneration/ODataCliCodeGenerationTests.cs Outdated
@habbes
Copy link
Copy Markdown
Contributor

habbes commented May 8, 2024

@gathogojr thanks for adding the tests. Do they run in the build pipeline as well?

@gathogojr gathogojr force-pushed the fix/390-fix-broken-command-line-options branch 2 times, most recently from dbeb23e to 89c7b8f Compare May 9, 2024 09:00
@gathogojr
Copy link
Copy Markdown
Contributor Author

@gathogojr thanks for adding the tests. Do they run in the build pipeline as well?

@habbes This was a good catch. I updated the build pipeline to run the CLI tests

@gathogojr gathogojr force-pushed the fix/390-fix-broken-command-line-options branch 3 times, most recently from 1dd129a to f1fdaa4 Compare May 13, 2024 04:41
Comment thread src/Microsoft.OData.Cli/GenerateCommand.cs Outdated
Comment thread src/Microsoft.OData.Cli/GenerateCommand.cs Outdated
Comment thread src/Microsoft.OData.Cli/GenerateCommand.cs Outdated
string namespacePrefix = generateOptions.NamespacePrefix?.Trim();
if (namespacePrefix == null)
{
namespacePrefix = configUserSettings?.NamespacePrefix;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Up there. you state that if namespace prefix is null then no namespace prefix should be applied. Why are we applying the namespace prefix here? or What does configUserSettings?.NamespacePrefix contain

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ElizabethOkerio My statement is different - "if namespace prefix is empty ...".
The namespace prefix will be null if namespace-prefix is not included in the command - in which case it should be okay to apply whatever is in the config file, and will empty if it's included in the command with an empty string - in which case we shouldn't apply whatever might be in the config file (and we'll save the updated config file with empty namespace prefix).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the reasoning here is that the namespacePrefix could either be set through command option or config file. If it's not set by the command option, we check the config file, if it's no set there either, it would be null. But also, I believe @gathogojr is making a clear distinction between empty and null. If the namespacePrefix is an empty string, then the user has explicitly set it to an empty value, which would remove the current prefix if it exists.

Comment thread src/Microsoft.OData.Cli/GenerateCommand.cs Outdated
@gathogojr gathogojr force-pushed the fix/390-fix-broken-command-line-options branch from f1fdaa4 to bf11e79 Compare May 13, 2024 08:49
@gathogojr gathogojr force-pushed the fix/390-fix-broken-command-line-options branch from bf11e79 to b743f65 Compare May 13, 2024 09:04
@gathogojr gathogojr merged commit 7d64bbe into OData:master May 13, 2024
@gathogojr gathogojr deleted the fix/390-fix-broken-command-line-options branch May 13, 2024 11:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants