Commit 8996a873 authored by Michael Herndon's avatar Michael Herndon

fix bugs in flex around lists/arrays

parent 9c875eb4
# escape=`
# Use the latest Windows Server Core image with .NET Framework 4.8.
FROM mcr.microsoft.com/dotnet/framework/sdk:4.8-windowsservercore-ltsc2019
# Restore the default Windows shell for correct batch processing.
SHELL ["cmd", "/S", "/C"]
# Download the Build Tools bootstrapper.
ADD https://aka.ms/vs/16/release/vs_buildtools.exe C:\TEMP\vs_buildtools.exe
# Install Build Tools excluding workloads and components with known issues.
RUN C:\TEMP\vs_buildtools.exe --quiet --wait --norestart --nocache `
--add Microsoft.VisualStudio.Workload.NetCoreBuildTools `
--add Microsoft.VisualStudio.Workload.WebBuildTools `
|| IF "%ERRORLEVEL%"=="3010" EXIT 0
# Start developer command prompt with any other commands specified.
#ENTRYPOINT C:\Vs\Common7\Tools\VsDevCmd.bat &&
# Default to PowerShell if no other command specified.
CMD ["powershell.exe", "-NoLogo", "-ExecutionPolicy", "Bypass"]
\ No newline at end of file
name: dotnet-ci
name: dotnet-ci-$(BUILD.BUILDNUMBER)
pool:
name: nm-ci-01
......
......@@ -38,7 +38,7 @@ namespace NerdyMishka.Flex
public override IList VisitArray(JArray value, FlexTypeDefinition definition)
{
var node = new JArray();
var node = value;
var def = definition;
if (!def.IsList)
throw new Exception("Mapping Mismatch");
......@@ -57,9 +57,9 @@ namespace NerdyMishka.Flex
var children= node.Children();
for (int i = 0; i < children.Count(); i++)
{
var nextNode = children[i];
var nextNode = (JToken)children[i];
var nextClassInfo = TypeInspector.GetTypeInfo(def.ValueType);
var obj = this.Visit(node, nextClassInfo);
var obj = this.Visit(nextNode, nextClassInfo, null);
list.Add(obj);
}
......@@ -113,7 +113,7 @@ namespace NerdyMishka.Flex
case JArray seq:
return this.VisitArray(seq, typeDef);
case JValue scalar:
return this.VisitProperty(scalar, propertyDef);
return this.VisitProperty(scalar, propertyDef, typeDef);
default:
throw new NotSupportedException($"{node.GetType().FullName}");
}
......@@ -281,12 +281,12 @@ namespace NerdyMishka.Flex
return new JValue(v) ;
}
public override object VisitProperty(JValue value, FlexPropertyDefinition definition)
public override object VisitProperty(JValue value, FlexPropertyDefinition definition, FlexTypeDefinition valueDefinition)
{
if(value.Type == JTokenType.Null )
return null;
return this.VisitValue(value.ToString(), definition);
return this.VisitValue(value.ToString(), definition, valueDefinition);
}
}
}
\ No newline at end of file
......@@ -16,7 +16,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="YamlDotNet" Version="5.2.1" />
<PackageReference Include="YamlDotNet" Version="6.1.2" />
</ItemGroup>
<ItemGroup>
......
......@@ -30,7 +30,7 @@ namespace NerdyMishka.Flex
{
var nextNode = this.Visit(item, valueTypeInfo);
if (nextNode != null)
node.Add(node);
node.Add(nextNode);
}
return node;
......@@ -38,10 +38,11 @@ namespace NerdyMishka.Flex
public override IList VisitArray(YamlSequenceNode value, FlexTypeDefinition definition)
{
var node = new YamlSequenceNode();
var node = value;
var def = definition;
if (!def.IsList)
throw new Exception("Mapping Mismatch");
if (!def.IsList && !def.IsArray)
throw new Exception($"Mapping Mismatch: {def.Type.FullName}");
IList list = null;
if (def.IsArray)
......@@ -58,7 +59,9 @@ namespace NerdyMishka.Flex
{
var nextNode = node.Children[i];
var nextClassInfo = TypeInspector.GetTypeInfo(def.ValueType);
var obj = this.Visit(node, nextClassInfo);
var obj = this.Visit(nextNode, nextClassInfo, null);
list.Add(obj);
}
......@@ -114,7 +117,7 @@ namespace NerdyMishka.Flex
case YamlSequenceNode seq:
return this.VisitArray(seq, typeDef);
case YamlScalarNode scalar:
return this.VisitProperty(scalar, propertyDef);
return this.VisitProperty(scalar, propertyDef, typeDef);
default:
throw new NotSupportedException($"{node.GetType().FullName}");
}
......@@ -287,9 +290,9 @@ namespace NerdyMishka.Flex
};
}
public override object VisitProperty(YamlScalarNode value, FlexPropertyDefinition definition)
public override object VisitProperty(YamlScalarNode value, FlexPropertyDefinition definition, FlexTypeDefinition valueDefinition)
{
var v = this.VisitValue(value.Value, definition);
var v = this.VisitValue(value.Value, definition, valueDefinition);
if(v is string && v == "null")
return null;
......
......@@ -10,14 +10,14 @@ namespace NerdyMishka.Flex.Yaml
{
var visitor = new YamlDotNetFlexVisitor(builder.Build());
var doc = visitor.VisitDocument(@object);
using(var fs = File.OpenWrite(builder.ResolvePath(file)))
using(var sw = new StreamWriter(fs))
using(var fs = System.IO.File.OpenWrite(file))
{
var ymlStream = new YamlDotNet.RepresentationModel.YamlStream(doc);
ymlStream.Save(sw);
var ymlStream = new YamlStream(doc);
ymlStream.Save(fs, false);
fs.Flush();
}
return builder;
}
......@@ -29,7 +29,8 @@ namespace NerdyMishka.Flex.Yaml
using(var sw = new StringWriter())
{
var ymlStream = new YamlStream(doc);
ymlStream.Save(sw);
ymlStream.Save(sw, false);
sw.Flush();
return sw.ToString();
}
......@@ -71,17 +72,8 @@ namespace NerdyMishka.Flex.Yaml
{
var visitor = new YamlDotNetFlexVisitor(builder.Build());
using(var fs = File.OpenRead(builder.ResolvePath(path)))
using(var sr = new StreamReader(fs))
{
var ymlStream = new YamlDotNet.RepresentationModel.YamlStream();
ymlStream.Load(sr);
if(ymlStream.Documents == null || ymlStream.Documents.Count == 0)
throw new System.Exception($"No yaml documents found in ${path}");
var doc = ymlStream.Documents[0];
return visitor.VisitObject<T>(doc);
}
var content = System.IO.File.ReadAllText(path);
return FromYaml<T>(builder, content);
}
}
}
\ No newline at end of file
......@@ -188,7 +188,7 @@ namespace NerdyMishka.Flex.Reflection
public abstract object VisitElement(TObject value, FlexTypeDefinition definition);
public abstract object VisitProperty(TValue value, FlexPropertyDefinition definition);
public abstract object VisitProperty(TValue value, FlexPropertyDefinition definition, FlexTypeDefinition valueDefinition);
public virtual object VisitValue(string value, FlexPropertyDefinition propertyDefinition, FlexTypeDefinition valueDefinition = null)
......@@ -225,13 +225,15 @@ namespace NerdyMishka.Flex.Reflection
}
return value.ToCharArray();
case "System.String":
if(value != null && value.ToLower() == "null")
return null;
if (propertyDefinition != null && propertyDefinition.IsEncrypted && cryptoProvider != null)
{
return cryptoProvider.DecryptString(value);
}
if(value != null && value.ToLower() == "null")
return null;
return value;
case "System.Security.SecureString":
......
......@@ -50,9 +50,10 @@ namespace NerdyMishka.Flex.Reflection
}
var interfaces = type.GetInterfaces();
var contractFound = false;
foreach (var contract in interfaces)
{
if (contract.IsGenericTypeDefinition)
if (contract.IsGenericType)
{
var typeDef = contract.GetGenericTypeDefinition();
if (typeDef == typeof(IDictionary<,>))
......@@ -61,6 +62,7 @@ namespace NerdyMishka.Flex.Reflection
info.KeyType = types[0];
info.ValueType = types[1];
info.IsList = true;
contractFound = true;
break;
}
......@@ -69,23 +71,32 @@ namespace NerdyMishka.Flex.Reflection
var types = contract.GetGenericArguments();
info.ValueType = types[0];
info.IsList = true;
contractFound = true;
break;
}
}
if (contract is IDictionary)
{
info.KeyType = typeof(object);
info.ValueType = typeof(object);
info.IsDictionary = true;
break;
}
}
if (contract is IList)
if(!contractFound)
{
foreach(var contract in interfaces)
{
info.ValueType = typeof(object);
info.IsList = true;
break;
if (contract is IDictionary)
{
info.KeyType = typeof(object);
info.ValueType = typeof(object);
info.IsDictionary = true;
break;
}
if (contract is IList)
{
info.ValueType = typeof(object);
info.IsList = true;
break;
}
}
}
......@@ -100,6 +111,9 @@ namespace NerdyMishka.Flex.Reflection
var properties = type.GetProperties();
foreach (var property in properties)
{
if(property.Name == "Item")
continue;
var propertyTypeInfo = new FlexPropertyDefinition()
{
Info = property
......@@ -143,6 +157,10 @@ namespace NerdyMishka.Flex.Reflection
symbolName = propertyTypeInfo.Name;
if(info.Properties.ContainsKey(symbolName))
throw new Exception($"key already exists {symbolName}");
info.Properties.Add(symbolName, propertyTypeInfo);
}
......
function Out-Json () {
[CmdletBinding(SupportsShouldProcess)]
Param(
[Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[Object] $InputObject,
[Alias("o", "dest")]
[ValidateNotNullOrEmpty]
[Parameter(Position = 0, ValueFromPipelineByPropertyName = $true)]
[String] $Destination,
[int32] $Depth = 10,
[Switch] $Compress,
[Switch] $Force
)
Process {
$Path = [IO.Path]::GetFullPath($Destination)
if ($PSCmdlet.ShouldProcess("InputObject", "Writing JSON to $Path")) {
$dir = Split-Path $Path
$createDirectory = $Force.ToBool()
$directoryExists = [IO.Directory]::Exists($dir)
if(!$createDirectory -and !$directoryExists) {
throw [IO.DirectoryNotFoundException] $dir
}
if(!$directoryExists) {
New-Item $dir -ItemType Directory | Write-Debug
}
$json = $null;
if(!($InputObject -is [String])) {
if($InputObject -is [array]) {
$json = ConvertTo-Json ([array]$InputObject) -Depth $Depth -Compress:$Compress
} else {
$json = ConvertTo-Json $InputObject -Depth $Depth -Compress:$Compress
}
} else {
$json = [string] $InputObject
}
# defaults to UTF8 with no Byte Order Mark ("BOM")
[IO.File]::WriteAllText($Path, $json)
}
}
}
\ No newline at end of file
......@@ -123,10 +123,6 @@ InModuleScope "Gz-Core" {
}
}
Remove-TestFiles -Name "fo"
}
......
function New-HtmlDocument() {
Param(
[Parameter(Position = 0)]
[String] $Path,
[Parameter(ValueFromPipeline = $true)]
[System.IO.FileInfo] $InputObject,
[Parameter(ValueFromPipeline = $true)]
[String] $Content
)
if([string]::IsNullOrWhiteSpace($Path) -and [string]::IsNullOrWhiteSpace($Content)) {
throw [System.ArgumentException] "Path or Content must be specified"
}
if($InputObject) {
if(!$InputObject.Exists) {
throw [System.IO.FileNotFoundException] $InputObject.FullName
}
$Path = $InputObject.FullName;
}
if(![string]::IsNullOrWhiteSpace($Path)) {
if(!(Test-Path $Path)) {
throw [System.IO.FileNotFoundException] $Path
}
$Content = Get-Content $Path -Raw
}
$Html = New-Object HtmlAgilityPack.HtmlDocument
$Html.LoadHtml($Content)
return $Html
}
\ No newline at end of file
function Select-Html() {
[CmdletBinding()]
Param(
[Parameter(ValueFromPipeline = $true, ParameterSetName="Content")]
[String] $Content,
[Parameter(ValueFromPipeline = $true, ParameterSetName = "Html")]
[HtmlAgilityPack.HtmlNode] $Html,
[Parameter(ValueFromPipeline = $true, ParameterSetName = "Response")]
[Microsoft.PowerShell.Commands.BasicHtmlWebResponseObject] $Response,
[PArameter(ValueFromPipeline = $true, ParameterSetName = "File")]
[System.IO.FileInfo] $InputObject,
[Parameter(Mandatory = $true)]
[String] $XPath,
[Switch] $NodesOnly
)
PROCESS {
if($Response) {
$Content = $Response.Content
}
$Path = $null
if($InputObject) {
if(!$InputObject.Exists) {
throw [System.IO.FileNotFoundException] $InputObject.FullName
}
$Path = $InputObject.FullName;
}
if(![string]::IsNullOrWhiteSpace($Path)) {
if(!(Test-Path $Path)) {
throw [System.IO.FileNotFoundException] $Path
}
$Content = Get-Content $Path -Raw
}
$Node = $null
if(![string]::IsNullOrWhiteSpace($Content)) {
$Doc = New-Object HtmlAgilityPack.HtmlDocument
$Doc.LoadHtml($Content)
$Node = $Doc.DocumentNode
}
if($Html) {
$Node = $Html
}
if(!$Node) {
throw [ArgumentException] "Html, Content, or Path should be specified"
}
$nodes = $Node.SelectNodes($XPath)
if($NodesOnly.ToBool()) {
return $nodes;
}
$results = @()
foreach($node in $nodes) {
$result = New-Object PsCustomObject -Property @{
Node = $node
Path = $p
Pattern = $XPath
}
$results += $result
}
return $results
}
}
\ No newline at end of file
$passwordCharSets = @{
LatinAlphaUpperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
LatinAlphaLowerCase = "abcdefghijklmnopqrstuvwxyz";
Digits = "0123456789";
Hyphen = "-";
Underscore = "_";
Brackets = "[]{}()<>";
Special = "~`&%$#@*+=|\/,:;^";
SpecialHybrid = "_-#|^{}$";
Space = " ";
}
function Merge-PasswordCharSets() {
[cmdletbinding()]
Param(
[ValidateSet('LatinAlphaUpperCase', 'LatinAlphaLowerCase', 'Digits', 'Hyphen', 'Underscore', 'Brackets', 'Special', 'Space', "SpecialHybrid")]
[Parameter()]
[string[]] $CharSets
)
if($null -eq $CharSets -or $CharSets.Length -eq 0) { return $null }
$result = $null;
$sb1 = New-Object System.Text.StringBuilder
foreach($setName in $CharSets) {
if($passwordCharSets.ContainsKey($setName)) {
$characters = $passwordCharSets[$setName];
$sb1.Append($characters) | Out-Null
}
}
$result = $sb1.ToString();
return $result;
}
\ No newline at end of file
function New-Password() {
<#
.SYNOPSIS
Generates a new cryptographically secure password as a char array.
.DESCRIPTION
Generates a new cryptographically secure password with a given length
using the supplied characters.
.PARAMETER Chars
An string of characters to use to build the password.
.PARAMETER CharSets
Optional. An array of choices that leverages a group of characters to use to build
the password. The default is 'LatinAlphaUpperCase', 'LatinAlphaLowerCase', 'Digits', 'SpecialHybrid'
.PARAMETER Length
Optional. The length of the password that is generated. The default is 16
.PARAMETER Validate
Optional. A script block that validates the password to ensure it meets
standards. The default checks for one uppercase, one lowercase, one digit,
one special character.
.PARAMETER AsSecureString
A flag that instructs the result to be returned as a SecureString.
.PARAMETER AsString
A flag that instructs the result to be returned as a string.
.EXAMPLE
PS C:\> $pw = New-Password -Length 16 -AsSecureString -CharSets 'LatinAlphaUpperCase', 'LatinAlphaLowerCase', 'Digits', 'SpecialHybrid'
Generates a new password with 16 characters with 1 upper, 1 lower, 1 digit, and one special character.
#>
Param(
[int] $Length,
[ValidateSet('LatinAlphaUpperCase', 'LatinAlphaLowerCase', 'Digits', 'Hyphen', 'Underscore', 'Brackets', 'Special', 'Space')]
[string[]] $CharSets = $null,
[string] $Chars = $null,
[ScriptBlock] $Validate = $null,
[Switch] $AsSecureString,
[Switch] $AsString
)
if($Length -eq 0) {
$Length = 16;
}
$sb = New-Object System.Text.StringBuilder
if(!$CharSets -and $CharSets.Length -gt 0) {
$set = (Merge-PasswordCharSets $CharSets)
if($set -and $set.Length -gt 0) {
$sb.Append($set) | Out-Null
}
}
if(![string]::IsNullOrWhiteSpace($Chars)) {
$sb.Append($Chars) | Out-Null
}
if($sb.Length -eq 0) {
$sets = Merge-PasswordCharSets -CharSets (@('LatinAlphaUpperCase', 'LatinAlphaLowerCase', 'Digits', "SpecialHybrid"))
$sb.Append($sets) | Out-Null
}
$permittedChars = $sb.ToString();
$password = [char[]]@(0) * $Length;
$bytes = [byte[]]@(0) * $Length;
while( (Test-Password $password -Validate $Validate) -eq $false) {
$rng = [System.Security.Cryptography.RandomNumberGenerator]::Create()
$rng.GetBytes($bytes);
$rng.Dispose();
for($i = 0; $i -lt $Length; $i++) {
$byte = $bytes[$i]
if($byte -eq 0) {
$index = 0;
} else {
$index = [int]($byte % $permittedChars.Length)
}
$index = [int] ($bytes[$i] % $permittedChars.Length);
$password[$i] = [char] $permittedChars[$index];
}
}
if($AsString.ToBool()) {
return (-join $password)
}
if($AsSecureString.ToBool()) {
$secureString = New-Object System.Security.SecureString;
foreach($char in $password) {
$secureString.AppendChar($char)
}
return $secureString;
}
# return char array as it is not immutable like a string
return $password;
}
\ No newline at end of file
function Test-Password() {
Param(
[Char[]] $Characters,
[ScriptBlock] $Validate
)
if(!$characters -or $characters.Length -eq 0) {
return $false;
}
if($Validate -ne $null) {
& $Validate -Characters $Characters;
}
$lower = $false;
$upper = $false;
$digit = $false;
$special = $false;
$others = "~`&%$#@*+=|\/,:;^_-[]{}()<> "
for($i = 0; $i -lt $characters.Length; $i++) {
if($lower -and $upper -and $digit -and $special) {
return $true;
}
$char = [char]$characters[$i];
if([Char]::IsDigit($char)) {
$digit = $true;
continue;
}
if([Char]::IsLetter($char)) {
if([Char]::IsUpper($char)) {
$upper = $true;
continue;
}
if([Char]::IsLower($char)) {
$lower = $true;
}
}
if($others.Contains($char)) {