diff --git a/MGR.CommandLineParser.sln b/MGR.CommandLineParser.sln index cdcb309..8da17ab 100644 --- a/MGR.CommandLineParser.sln +++ b/MGR.CommandLineParser.sln @@ -41,6 +41,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MGR.CommandLineParser.Hosti EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "docs", "docs\docs.csproj", "{405858FA-1E78-48C7-9915-B558D0F15CAE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MGR.CommandLineParser.Command.OracleProcedure", "src\MGR.CommandLineParser.Command.OracleProcedure\MGR.CommandLineParser.Command.OracleProcedure.csproj", "{738CC3C0-16C1-45FB-B1D0-A21E8FA9D7D9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -79,6 +81,10 @@ Global {405858FA-1E78-48C7-9915-B558D0F15CAE}.Debug|Any CPU.Build.0 = Debug|Any CPU {405858FA-1E78-48C7-9915-B558D0F15CAE}.Release|Any CPU.ActiveCfg = Release|Any CPU {405858FA-1E78-48C7-9915-B558D0F15CAE}.Release|Any CPU.Build.0 = Release|Any CPU + {738CC3C0-16C1-45FB-B1D0-A21E8FA9D7D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {738CC3C0-16C1-45FB-B1D0-A21E8FA9D7D9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {738CC3C0-16C1-45FB-B1D0-A21E8FA9D7D9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {738CC3C0-16C1-45FB-B1D0-A21E8FA9D7D9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -92,6 +98,7 @@ Global {9F5A0142-E0A1-45A2-961E-55611B4440AD} = {FB795A12-C939-492F-9377-4C468D01EF3C} {40EAA8E2-7AFE-4ED1-A961-35BD7E424985} = {FB795A12-C939-492F-9377-4C468D01EF3C} {405858FA-1E78-48C7-9915-B558D0F15CAE} = {172E24C2-BF99-4DA9-A7DF-8C0BB44C138D} + {738CC3C0-16C1-45FB-B1D0-A21E8FA9D7D9} = {FB795A12-C939-492F-9377-4C468D01EF3C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {827031C5-AE76-4B4C-9503-E13F15B497E9} diff --git a/docs/procedure/oracle/index.md b/docs/procedure/oracle/index.md new file mode 100644 index 0000000..ca28fa6 --- /dev/null +++ b/docs/procedure/oracle/index.md @@ -0,0 +1,7 @@ +# Use Oracle procedure commands + +You can use stored procedure from Oracle database to represent your commands. + +This allow you to call stored procedures via the parser +(to create command-line or web-based tool). + diff --git a/src/MGR.CommandLineParser.Command.OracleProcedure/CommandMetadata.cs b/src/MGR.CommandLineParser.Command.OracleProcedure/CommandMetadata.cs new file mode 100644 index 0000000..58a60f7 --- /dev/null +++ b/src/MGR.CommandLineParser.Command.OracleProcedure/CommandMetadata.cs @@ -0,0 +1,23 @@ +using MGR.CommandLineParser.Extensibility.Command; + +namespace MGR.CommandLineParser.Command.OracleProcedure +{ + internal class CommandMetadata : ICommandMetadata + { + public CommandMetadata(string name) + { + Name = name; + Description = name; + Samples = new string[0]; + } + public string Name { get; } + + public string Description { get; } + + public string Usage => string.Empty; + + public string[] Samples { get; } + + public bool HideFromHelpListing => false; + } +} diff --git a/src/MGR.CommandLineParser.Command.OracleProcedure/CommandObjectBuilder.cs b/src/MGR.CommandLineParser.Command.OracleProcedure/CommandObjectBuilder.cs new file mode 100644 index 0000000..a7ce960 --- /dev/null +++ b/src/MGR.CommandLineParser.Command.OracleProcedure/CommandObjectBuilder.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Data.Common; +using MGR.CommandLineParser.Extensibility.Command; + +namespace MGR.CommandLineParser.Command.OracleProcedure +{ + internal class CommandObjectBuilder : ICommandObjectBuilder + { + private IEnumerable _options; + private IEnumerable _outParameters; + private DbConnection _dbConnection; + + public CommandObjectBuilder(IEnumerable options, IEnumerable outParameters, DbConnection dbConnection) + { + _options = options; + _outParameters = outParameters; + _dbConnection = dbConnection; + } + + public void AddArguments(string argument) => throw new NotImplementedException("Oracle procedures does not define unamed arguments"); + public ICommandOption FindOption(string optionName) + { + throw new NotImplementedException(); + } + + public ICommandOption FindOptionByShortName(string optionShortName) + { + throw new NotImplementedException($"Oracle procedures does not define 'short name' for the parameters ('{ optionShortName }')"); + } + + public ICommandObject GenerateCommandObject() + { + throw new NotImplementedException(); + } + + public CommandValidationResult Validate(IServiceProvider serviceProvider) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/MGR.CommandLineParser.Command.OracleProcedure/CommandOption.cs b/src/MGR.CommandLineParser.Command.OracleProcedure/CommandOption.cs new file mode 100644 index 0000000..64783f6 --- /dev/null +++ b/src/MGR.CommandLineParser.Command.OracleProcedure/CommandOption.cs @@ -0,0 +1,14 @@ +using System; +using MGR.CommandLineParser.Extensibility.Command; + +namespace MGR.CommandLineParser.Command.OracleProcedure +{ + internal class CommandOption : ICommandOption + { + public bool ShouldProvideValue => throw new NotImplementedException(); + + public ICommandOptionMetadata Metadata => throw new NotImplementedException(); + + public void AssignValue(string optionValue) => throw new NotImplementedException(); + } +} diff --git a/src/MGR.CommandLineParser.Command.OracleProcedure/CommandOptionMetadata.cs b/src/MGR.CommandLineParser.Command.OracleProcedure/CommandOptionMetadata.cs new file mode 100644 index 0000000..a705c96 --- /dev/null +++ b/src/MGR.CommandLineParser.Command.OracleProcedure/CommandOptionMetadata.cs @@ -0,0 +1,21 @@ +using MGR.CommandLineParser.Extensibility.Command; + +namespace MGR.CommandLineParser.Command.OracleProcedure +{ + internal class CommandOptionMetadata : ICommandOptionMetadata + { + public CommandOptionMetadata(OptionDisplayInfo displayInfo, bool isRequired, string defaultValue) + { + DisplayInfo = displayInfo; + IsRequired = isRequired; + DefaultValue = defaultValue; + } + public bool IsRequired { get; } + + public CommandOptionCollectionType CollectionType => CommandOptionCollectionType.None; + + public IOptionDisplayInfo DisplayInfo { get; } + + public string DefaultValue { get; } + } +} \ No newline at end of file diff --git a/src/MGR.CommandLineParser.Command.OracleProcedure/CommandType.cs b/src/MGR.CommandLineParser.Command.OracleProcedure/CommandType.cs new file mode 100644 index 0000000..321d151 --- /dev/null +++ b/src/MGR.CommandLineParser.Command.OracleProcedure/CommandType.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Data.Common; +using MGR.CommandLineParser.Extensibility.Command; + +namespace MGR.CommandLineParser.Command.OracleProcedure +{ + internal class CommandType : ICommandType + { + private readonly IEnumerable _outParameters; + private readonly DbConnection _dbConnection; + + public CommandType(CommandMetadata commandMetadata, IEnumerable options, IEnumerable outParameters, DbConnection dbConnection) + { + Metadata = commandMetadata; + Options = options; + _outParameters = outParameters; + _dbConnection = dbConnection; + } + public ICommandMetadata Metadata { get; } + + public IEnumerable Options { get; } + + public ICommandObjectBuilder CreateCommandObjectBuilder(IServiceProvider serviceProvider) + { + return new CommandObjectBuilder(Options, _outParameters, _dbConnection); + } + } +} diff --git a/src/MGR.CommandLineParser.Command.OracleProcedure/CommandTypeProvider.cs b/src/MGR.CommandLineParser.Command.OracleProcedure/CommandTypeProvider.cs new file mode 100644 index 0000000..4789275 --- /dev/null +++ b/src/MGR.CommandLineParser.Command.OracleProcedure/CommandTypeProvider.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using System.Data.Common; +using System.Linq; +using System.Threading.Tasks; +using MGR.CommandLineParser.Extensibility.Command; +using Microsoft.EntityFrameworkCore; + +namespace MGR.CommandLineParser.Command.OracleProcedure +{ + internal class CommandTypeProvider : ICommandTypeProvider + { + private readonly OracleSystemContext _dbContext; + + public CommandTypeProvider(OracleSystemContext dbContext) + { + _dbContext = dbContext; + } + public async Task> GetAllCommandTypes() + { + var procedures = await _dbContext.Procedures.Include(procedure => procedure.Parameters).ToListAsync(); + return procedures.Select(procedure => MapProcedureToCommandType(procedure, _dbContext.Database.GetDbConnection())); + } + public async Task GetCommandType(string commandName) + { + var procedure = await _dbContext.Procedures.Include(procedure => procedure.Parameters).Where(procedure => procedure.Name == commandName).SingleOrDefaultAsync(); + if (procedure != null) + { + return MapProcedureToCommandType(procedure, _dbContext.Database.GetDbConnection()); + } + return null; + } + + private static CommandType MapProcedureToCommandType(Procedure procedure, DbConnection dbConnection) + { + return new CommandType( + new CommandMetadata(procedure.Name), + procedure.Parameters + .Where(parameter => parameter.Direction.HasFlag(Direction.In)) + .Select(parameter => new CommandOptionMetadata( + new OptionDisplayInfo(parameter.Name), + !parameter.HasDefaultValue, + parameter.DefaultValue + ) + ), + procedure.Parameters + .Where(parameter => parameter.Direction.HasFlag(Direction.Out)), + dbConnection + ); + } + } +} diff --git a/src/MGR.CommandLineParser.Command.OracleProcedure/Direction.cs b/src/MGR.CommandLineParser.Command.OracleProcedure/Direction.cs new file mode 100644 index 0000000..e5e7845 --- /dev/null +++ b/src/MGR.CommandLineParser.Command.OracleProcedure/Direction.cs @@ -0,0 +1,12 @@ +using System; + +namespace MGR.CommandLineParser.Command.OracleProcedure +{ + [Flags] + internal enum Direction + { + In = 1, + Out = 2, + InOut = 3 + } +} \ No newline at end of file diff --git a/src/MGR.CommandLineParser.Command.OracleProcedure/MGR.CommandLineParser.Command.OracleProcedure.csproj b/src/MGR.CommandLineParser.Command.OracleProcedure/MGR.CommandLineParser.Command.OracleProcedure.csproj new file mode 100644 index 0000000..48524fa --- /dev/null +++ b/src/MGR.CommandLineParser.Command.OracleProcedure/MGR.CommandLineParser.Command.OracleProcedure.csproj @@ -0,0 +1,15 @@ + + + + netstandard2.0 + + + + + + + + + + + diff --git a/src/MGR.CommandLineParser.Command.OracleProcedure/Mapping/ParameterMap.cs b/src/MGR.CommandLineParser.Command.OracleProcedure/Mapping/ParameterMap.cs new file mode 100644 index 0000000..7e92179 --- /dev/null +++ b/src/MGR.CommandLineParser.Command.OracleProcedure/Mapping/ParameterMap.cs @@ -0,0 +1,44 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace MGR.CommandLineParser.Command.OracleProcedure.Mapping +{ + internal class ParameterMap : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("USER_ARGUMENTS"); + + builder.Property(_ => _.Name) + .HasColumnName("OBJECT_NAME"); + + var defaultValueConverter = new BoolToStringConverter("N", "Y"); + builder.Property(_ => _.HasDefaultValue) + .HasColumnName("DEFAULTED") + .HasConversion(defaultValueConverter); + + builder.Property(_ => _.DefaultValue) + .HasColumnName("DEFAULT_VALUE"); + + builder.Property(_ => _.DataType) + .HasColumnName("DATA_TYPE"); + + var directionConverter = new ValueConverter( + _ => _.ToString(), + value => ParseDirection(value)); + builder.Property(_ => _.Direction) + .HasColumnName("IN_OUT") + .HasConversion(directionConverter); + } + private static Direction ParseDirection(string value) => value + switch + { + "IN" => Direction.In, + "OUT" => Direction.Out, + "IN/OUT" => Direction.InOut, + _ => throw new ArgumentOutOfRangeException(nameof(value)) + }; + } +} \ No newline at end of file diff --git a/src/MGR.CommandLineParser.Command.OracleProcedure/Mapping/ProcedureMap.cs b/src/MGR.CommandLineParser.Command.OracleProcedure/Mapping/ProcedureMap.cs new file mode 100644 index 0000000..9d4ac1b --- /dev/null +++ b/src/MGR.CommandLineParser.Command.OracleProcedure/Mapping/ProcedureMap.cs @@ -0,0 +1,18 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace MGR.CommandLineParser.Command.OracleProcedure.Mapping +{ + internal class ProcedureMap : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("ALL_OBJETS"); + + builder.Property(_ => _.Name) + .HasColumnName("OBJECT_NAME"); + + builder.HasQueryFilter(_ => EF.Property(_, "OBJECT_TYPE") == "PROCEDURE"); + } + } +} \ No newline at end of file diff --git a/src/MGR.CommandLineParser.Command.OracleProcedure/OptionDisplayInfo.cs b/src/MGR.CommandLineParser.Command.OracleProcedure/OptionDisplayInfo.cs new file mode 100644 index 0000000..deeb241 --- /dev/null +++ b/src/MGR.CommandLineParser.Command.OracleProcedure/OptionDisplayInfo.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using System.Linq; +using MGR.CommandLineParser.Extensibility.Command; + +namespace MGR.CommandLineParser.Command.OracleProcedure +{ + internal class OptionDisplayInfo : IOptionDisplayInfo + { + public OptionDisplayInfo(string name) + { + Name = name; + Description = name; + } + public string Name { get; } + + public IEnumerable AlternateNames => Enumerable.Empty(); + + public string ShortName => string.Empty; + + public string Description { get; } + } +} diff --git a/src/MGR.CommandLineParser.Command.OracleProcedure/OracleSystemContext.cs b/src/MGR.CommandLineParser.Command.OracleProcedure/OracleSystemContext.cs new file mode 100644 index 0000000..fd6f8f5 --- /dev/null +++ b/src/MGR.CommandLineParser.Command.OracleProcedure/OracleSystemContext.cs @@ -0,0 +1,22 @@ +using MGR.CommandLineParser.Command.OracleProcedure.Mapping; +using Microsoft.EntityFrameworkCore; + +namespace MGR.CommandLineParser.Command.OracleProcedure +{ + internal sealed class OracleSystemContext : DbContext + { + public DbSet Procedures { get; set; } + + public OracleSystemContext(DbContextOptions options) + : base(options) + { } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder + .ApplyConfiguration(new ProcedureMap()) + .ApplyConfiguration(new ParameterMap()); + base.OnModelCreating(modelBuilder); + } + } +} diff --git a/src/MGR.CommandLineParser.Command.OracleProcedure/Parameter.cs b/src/MGR.CommandLineParser.Command.OracleProcedure/Parameter.cs new file mode 100644 index 0000000..b8a4568 --- /dev/null +++ b/src/MGR.CommandLineParser.Command.OracleProcedure/Parameter.cs @@ -0,0 +1,11 @@ +namespace MGR.CommandLineParser.Command.OracleProcedure +{ + internal class Parameter + { + public string Name { get; set; } + public bool HasDefaultValue { get; set; } + public string DefaultValue { get; set; } + public string DataType { get; set; } + public Direction Direction { get; set; } + } +} \ No newline at end of file diff --git a/src/MGR.CommandLineParser.Command.OracleProcedure/Procedure.cs b/src/MGR.CommandLineParser.Command.OracleProcedure/Procedure.cs new file mode 100644 index 0000000..26f7d84 --- /dev/null +++ b/src/MGR.CommandLineParser.Command.OracleProcedure/Procedure.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace MGR.CommandLineParser.Command.OracleProcedure +{ + internal class Procedure + { + public string Name { get; set; } + public IEnumerable Parameters { get; set; } + } +} \ No newline at end of file