PowerShell/src/Modules/Windows-Full/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataAdapter.ps1
PowerShell Team c748652c34 Copy all mapped files from [SD:725290]
commit 8cec8f150da7583b7af5efbe2853efee0179750c
2016-07-28 23:23:03 -07:00

2071 lines
167 KiB
PowerShell
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Import-LocalizedData LocalizedData -FileName Microsoft.PowerShell.ODataUtilsStrings.psd1
# Add .NET classes used by the module
Add-Type -TypeDefinition $global:BaseClassDefinitions
#########################################################
# Generates PowerShell module containing client side
# proxy cmdlets that can be used to interact with an
# OData based server side endpoint.
#########################################################
function ExportODataEndpointProxy
{
param
(
[string] $Uri,
[string] $OutputModule,
[string] $MetadataUri,
[PSCredential] $Credential,
[string] $CreateRequestMethod,
[string] $UpdateRequestMethod,
[string] $CmdletAdapter,
[Hashtable] $ResourceNameMapping,
[switch] $Force,
[Hashtable] $CustomData,
[switch] $AllowClobber,
[switch] $AllowUnsecureConnection,
[Hashtable] $Headers,
[string] $ProgressBarStatus,
[System.Management.Automation.PSCmdlet] $PSCmdlet
)
[xml] $metadataXML = GetMetaData $MetadataUri $PSCmdlet $Credential $Headers
[ODataUtils.Metadata] $metaData = ParseMetadata $metadataXML $MetadataUri $CmdletAdapter $PSCmdlet
VerifyMetaData $MetadataUri $metaData $AllowClobber.IsPresent $PSCmdlet $progressBarStatus $CmdletAdapter $CustomData $ResourceNameMapping
GenerateClientSideProxyModule $metaData $MetadataUri $Uri $OutputModule $CreateRequestMethod $UpdateRequestMethod $CmdletAdapter $ResourceNameMapping $CustomData $ProgressBarStatus $PSCmdlet
}
#########################################################
# ParseMetaData is a helper function used to parse the
# metadata to convert it in to an object structure for
# further consumption during proxy generation.
#########################################################
function ParseMetaData
{
param
(
[xml] $metadataXml,
[string] $metaDataUri,
[string] $cmdletAdapter,
[System.Management.Automation.PSCmdlet] $callerPSCmdlet
)
# $metaDataUri is already validated at the cmdlet layer.
if($callerPSCmdlet -eq $null) { throw ($LocalizedData.ArguementNullError -f "PSCmdlet", "ParseMetadata") }
if($metadataXml -eq $null)
{
$errorMessage = ($LocalizedData.InValidXmlInMetadata -f $metaDataUri)
$exception = [System.InvalidOperationException]::new($errorMessage, $_.Exception)
$errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidMetadataUriFormat" $null ([System.Management.Automation.ErrorCategory]::InvalidArgument) $exception $metaDataUri
$callerPSCmdlet.ThrowTerminatingError($errorRecord)
}
Write-Verbose $LocalizedData.VerboseParsingMetadata
# Check the OData version in the fetched metadata to make sure that
# OData version (and hence the protocol) used in the metadata is
# supported by the adatapter used for executing the generated
# proxy cmdlets.
if(($metadataXML -ne $null) -and ($metadataXML.Edmx -ne $null))
{
if($null -eq $metadataXML.Edmx.Version)
{
$errorMessage = ($LocalizedData.ODataVersionNotFound -f $MetadataUri)
$exception = [System.InvalidOperationException]::new($errorMessage)
$errorRecord = CreateErrorRecordHelper "ODataEndpointProxyODataVersionNotFound" $null ([System.Management.Automation.ErrorCategory]::InvalidData) $exception $MetadataUri
$callerPSCmdlet.ThrowTerminatingError($errorRecord)
}
$metaDataVersion = New-Object -TypeName System.Version -ArgumentList @($metadataXML.Edmx.Version)
# When we support plug-in model, We would have to fetch the
# $minSupportedVersionString & $maxSupportedVersionString
# from the plug-in instead of using an hardcoded value.
$minSupportedVersionString = '1.0'
$maxSupportedVersionString = '3.0'
$minSupportedVersion = New-Object -TypeName System.Version -ArgumentList @($minSupportedVersionString)
$maxSupportedVersion = New-Object -TypeName System.Version -ArgumentList @($maxSupportedVersionString)
$minVersionComparisonResult = $minSupportedVersion.CompareTo($metaDataVersion)
$maxVersionComparisonResult = $maxSupportedVersion.CompareTo($metaDataVersion)
if(-not($minVersionComparisonResult -lt $maxVersionComparisonResult))
{
$errorMessage = ($LocalizedData.ODataVersionNotSupported -f $metadataXML.Edmx.Version, $MetadataUri, $minSupportedVersionString, $maxSupportedVersionString, $CmdletAdapter)
$exception = [System.NotSupportedException]::new($errorMessage)
$errorRecord = CreateErrorRecordHelper "ODataEndpointProxyODataVersionNotSupported" $null ([System.Management.Automation.ErrorCategory]::InvalidData) $exception $MetadataUri
$callerPSCmdlet.ThrowTerminatingError($errorRecord)
}
}
else
{
$errorMessage = ($LocalizedData.InValidMetadata -f $MetadataUri)
$exception = [System.InvalidOperationException]::new($errorMessage)
$errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidMetadata" $null ([System.Management.Automation.ErrorCategory]::InvalidData) $exception $MetadataUri
$callerPSCmdlet.ThrowTerminatingError($errorRecord)
}
foreach ($schema in $MetadataXML.Edmx.DataServices.Schema)
{
if (($schema -ne $null) -and [string]::IsNullOrEmpty($schema.NameSpace ))
{
$callerPSCmdlet = $callerPSCmdlet -as [System.Management.Automation.PSCmdlet]
$errorMessage = ($LocalizedData.InValidSchemaNamespace -f $metaDataUri)
$exception = [System.InvalidOperationException]::new($errorMessage)
$errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidSchemaNamespace" $null ([System.Management.Automation.ErrorCategory]::InvalidArgument) $exception $metaDataUri
$callerPSCmdlet.ThrowTerminatingError($errorRecord)
}
}
$metaData = New-Object -TypeName ODataUtils.Metadata
# this is a processing queue for those types that require base types that haven't been defined yet
$entityAndComplexTypesQueue = @{}
foreach ($schema in $metadataXml.Edmx.DataServices.Schema)
{
if ($schema -eq $null)
{
Write-Error $LocalizedData.EmptySchema
continue
}
if ($metadata.Namespace -eq $null)
{
$metaData.Namespace = $schema.Namespace
}
foreach ($entityType in $schema.EntityType)
{
$baseType = $null
if ($entityType.BaseType -ne $null)
{
# add it to the processing queue
$baseType = GetBaseType $entityType $metaData
if ($baseType -eq $null)
{
$entityAndComplexTypesQueue[$entityType.BaseType] += @(@{type='EntityType'; value=$entityType})
continue
}
}
[ODataUtils.EntityType] $newType = ParseMetadataTypeDefinition $entityType $baseType $metaData $schema.Namespace $true
$metaData.EntityTypes += $newType
AddDerivedTypes $newType $entityAndComplexTypesQueue $metaData $schema.Namespace
}
foreach ($complexType in $schema.ComplexType)
{
$baseType = $null
if ($complexType.BaseType -ne $null)
{
# add it to the processing queue
$baseType = GetBaseType $complexType $metaData
if ($baseType -eq $null)
{
$entityAndComplexTypesQueue[$entityType.BaseType] += @(@{type='ComplexType'; value=$complexType})
continue
}
}
[ODataUtils.EntityType] $newType = ParseMetadataTypeDefinition $complexType $baseType $metaData $schema.Namespace $false
$metaData.ComplexTypes += $newType
AddDerivedTypes $newType $entityAndComplexTypesQueue $metaData $schema.Namespace
}
}
foreach ($schema in $metadataXml.Edmx.DataServices.Schema)
{
foreach ($entityContainer in $schema.EntityContainer)
{
if ($entityContainer.IsDefaultEntityContainer)
{
$metaData.DefaultEntityContainerName = $entityContainer.Name
}
$entityTypeToEntitySetMapping = @{};
foreach ($entitySet in $entityContainer.EntitySet)
{
$entityType = $metaData.EntityTypes | Where-Object { $_.Name -eq $entitySet.EntityType.Split('.')[-1] }
$entityTypeName = $entityType.Name
if($entityTypeToEntitySetMapping.ContainsKey($entityTypeName))
{
$existingEntitySetName = $entityTypeToEntitySetMapping[$entityTypeName]
$errorMessage = ($LocalizedData.EntityNameConflictError -f $metaDataUri, $existingEntitySetName, $entitySet.Name, $entityTypeName)
$exception = [System.NotSupportedException]::new($errorMessage)
$errorRecord = CreateErrorRecordHelper "ODataEndpointProxyEntityTypeMappingError" $null ([System.Management.Automation.ErrorCategory]::InvalidData) $exception $metaDataUri
$callerPSCmdlet.ThrowTerminatingError($errorRecord)
}
else
{
$entityTypeToEntitySetMapping.Add($entityTypeName, $entitySet.Name)
}
$newEntitySet = [ODataUtils.EntitySet] @{
"Namespace" = $schema.Namespace;
"Name" = $entitySet.Name;
"Type" = $entityType;
}
$metaData.EntitySets += $newEntitySet
}
}
}
foreach ($schema in $metadataXml.Edmx.DataServices.Schema)
{
foreach ($association in $schema.Association)
{
$newAssociationType = [ODataUtils.AssociationType] @{
"Namespace" = $schema.Namespace;
"EndType1" = $metaData.EntityTypes | Where-Object { $_.Name -eq $association.End[0].Type.Split('.')[-1] };
"NavPropertyName1" = $association.End[0].Role;
"Multiplicity1" = $association.End[0].Multiplicity;
"EndType2" = $metaData.EntityTypes | Where-Object { $_.Name -eq $association.End[1].Type.Split('.')[-1] };
"NavPropertyName2" = $association.End[1].Role;
"Multiplicity2" = $association.End[1].Multiplicity;
}
$newAssociation = [ODataUtils.AssociationSet] @{
"Namespace" = $schema.Namespace;
"Name" = $association.Name;
"Type" = $newAssociationType;
}
$metaData.Associations += $newAssociation
}
}
foreach ($schema in $metadataXml.Edmx.DataServices.Schema)
{
foreach ($action in $schema.EntityContainer.FunctionImport)
{
# HttpMethod is only used for legacy Service Operations
if ($action.HttpMethod -eq $null)
{
if ($action.IsSideEffecting -ne $null)
{
$isSideEffecting = $action.IsSideEffecting
}
else
{
$isSideEffecting = $true
}
$newAction = [ODataUtils.Action] @{
"Namespace" = $schema.Namespace;
"Verb" = $action.Name;
"IsSideEffecting" = $isSideEffecting;
"IsBindable" = $action.IsBindable;
# we don't care about IsAlwaysBindable, since we populate actions information from $metaData
# so we can't know the state of the entity
}
# Actions are always SideEffecting, otherwise it's an OData function
if ($newAction.IsSideEffecting -ne $false)
{
foreach ($parameter in $action.Parameter)
{
if ($parameter.Nullable -ne $null)
{
$parameterIsNullable = [System.Convert]::ToBoolean($parameter.Nullable);
}
$newParameter = [ODataUtils.TypeProperty] @{
"Name" = $parameter.Name;
"TypeName" = $parameter.Type;
"IsNullable" = $parameterIsNullable
}
$newAction.Parameters += $newParameter
}
# IsBindable means it operates on Entity/ies
if ($newAction.IsBindable)
{
$regex = "Collection\((.+)\)"
if ($newAction.Parameters[0].TypeName -match $regex)
{
# action operating on a collection of entities
$insideTypeName = Convert-ODataTypeToCLRType $Matches[1]
$newAction.EntitySet = $metaData.EntitySets | Where-Object { ($_.Type.Namespace + "." + $_.Type.Name) -eq $insideTypeName }
$newAction.IsSingleInstance = $false
}
else
{
# actions operating on a single instance
$newAction.EntitySet = $metaData.EntitySets | Where-Object { ($_.Type.Namespace + "." + $_.Type.Name) -eq $newAction.Parameters[0].TypeName }
$newAction.IsSingleInstance = $true
}
}
$metaData.Actions += $newAction
}
}
}
}
$metaData
}
#########################################################
# VerifyMetaData is a helper function used to validate
# the processed metadata to make sure client side proxy
# can be created for the supplied metadata.
#########################################################
function VerifyMetaData
{
param
(
[string] $metaDataUri,
[ODataUtils.Metadata] $metaData,
[boolean] $allowClobber,
[System.Management.Automation.PSCmdlet] $callerPSCmdlet,
[string] $progressBarStatus,
[string] $cmdletAdapter,
[Hashtable] $customData,
[Hashtable] $resourceNameMapping
)
# $metaDataUri & $cmdletAdapter is already validated at the cmdlet layer.
if($metaData -eq $null) { throw ($LocalizedData.ArguementNullError -f "metadata", "VerifyMetaData") }
if($callerPSCmdlet -eq $null) { throw ($LocalizedData.ArguementNullError -f "PSCmdlet", "VerifyMetaData") }
if($progressBarStatus -eq $null) { throw ($LocalizedData.ArguementNullError -f "ProgressBarStatus", "VerifyMetaData") }
Write-Verbose $LocalizedData.VerboseVerifyingMetadata
if ($metadata.EntitySets.Count -le 0)
{
$errorMessage = ($LocalizedData.NoEntitySets -f $metaDataUri)
$exception = [System.InvalidOperationException]::new($errorMessage)
$errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidMetaDataUri" $null ([System.Management.Automation.ErrorCategory]::InvalidArgument) $exception $metaDataUri
$callerPSCmdlet.ThrowTerminatingError($errorRecord)
}
if ($metadata.EntityTypes.Count -le 0)
{
$errorMessage = ($LocalizedData.NoEntityTypes -f $metaDataUri)
$exception = [System.InvalidOperationException]::new($errorMessage)
$errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidMetaDataUri" $null ([System.Management.Automation.ErrorCategory]::InvalidArgument) $exception $metaDataUri
$callerPSCmdlet.ThrowTerminatingError($errorRecord)
}
# All the generated proxy cmdlets would have the following paramters added.
# The ODataAdpter has the default implementation on how to handle the
# scenario when these parameters are used during proxy invocations.
# The default implementaion can be overridden using adapter derivation model.
$reservedProperties = @("Filter", "IncludeTotalResponseCount", "OrderBy", "Select", "Skip", "Top", "ConnectionUri", "CertificateThumbprint", "Credential")
$validEntitySets = @()
$sessionCommands = Get-Command -All
foreach ($entitySet in $metaData.EntitySets)
{
if ($entitySet.Type -eq $null)
{
$errorMessage = ($LocalizedData.EntitySetUndefinedType -f $metaDataUri, $entitySet.Name)
$exception = [System.InvalidOperationException]::new($errorMessage)
$errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidMetaDataUri" $null ([System.Management.Automation.ErrorCategory]::InvalidArgument) $exception $metaDataUri
$callerPSCmdlet.ThrowTerminatingError($errorRecord)
}
if ($cmdletAdapter -eq "NetworkControllerAdapter" -And $customData -And $customData.Contains($entitySet.Name) -eq $false)
{
continue
}
$hasConflictingProperty = $false
$hasConflictingCommand = $false
$entityAndNavigationProperties = (GetAllProperties $entitySet.Type) + (GetAllProperties $entitySet.Type -IncludeOnlyNavigationProperties)
foreach($entityProperty in $entityAndNavigationProperties)
{
if($reservedProperties.Contains($entityProperty.Name))
{
$hasConflictingProperty = $true
if(!$allowClobber)
{
# Write Error message and skip current Entity Set.
$errorMessage = ($LocalizedData.SkipEntitySetProxyCreation -f $entitySet.Name, $entitySet.Type.Name, $entityProperty.Name)
$exception = [System.InvalidOperationException]::new($errorMessage)
$errorRecord = CreateErrorRecordHelper "ODataEndpointDefaultPropertyCollision" $null ([System.Management.Automation.ErrorCategory]::InvalidOperation) $exception $metaDataUri
$callerPSCmdlet.WriteError($errorRecord)
}
else
{
$warningMessage = ($LocalizedData.EntitySetProxyCreationWithWarning -f $entitySet.Name, $entityProperty.Name, $entitySet.Type.Name)
$callerPSCmdlet.WriteWarning($warningMessage)
}
}
}
foreach($currentCommand in $sessionCommands)
{
# The generated command Noun can be set using ResourceNameMapping
$generatedCommandName = $entitySet.Name
if ($resourceNameMapping -And $resourceNameMapping.Contains($entitySet.Name)) {
$generatedCommandName = $resourceNameMapping[$entitySet.Name]
}
if(($currentCommand.Noun -ne $null -and $currentCommand.Noun -eq $generatedCommandName) -and
($currentCommand.Verb -eq "Get" -or
$currentCommand.Verb -eq "Set" -or
$currentCommand.Verb -eq "New" -or
$currentCommand.Verb -eq "Remove"))
{
$hasConflictingCommand = $true
VerifyMetadataHelper $LocalizedData.SkipEntitySetConflictCommandCreation `
$LocalizedData.EntitySetConflictCommandCreationWithWarning `
$entitySet.Name $currentCommand.Name $metaDataUri $allowClobber $callerPSCmdlet
}
}
foreach($currentAction in $metaData.Actions)
{
$actionCommand = "Invoke-" + "$($entitySet.Name)$($currentAction.Verb)"
foreach($currentCommand in $sessionCommands)
{
if($actionCommand -eq $currentCommand.Name)
{
$hasConflictingCommand = $true
VerifyMetadataHelper $LocalizedData.SkipEntitySetConflictCommandCreation `
$LocalizedData.EntitySetConflictCommandCreationWithWarning $entitySet.Name `
$currentCommand.Name $metaDataUri $allowClobber $callerPSCmdlet
}
}
}
if(!($hasConflictingProperty -or $hasConflictingCommand)-or $allowClobber)
{
$validEntitySets += $entitySet
}
}
if ($cmdletAdapter -ne "NetworkControllerAdapter") {
$metaData.EntitySets = $validEntitySets
$validServiceActions = @()
$hasConflictingServiceActionCommand = $true
foreach($currentAction in $metaData.Actions)
{
$serviceActionCommand = "Invoke-" + "$($currentAction.Verb)"
foreach($currentCommand in $sessionCommands)
{
if($serviceActionCommand -eq $currentCommand.Name)
{
$hasConflictingServiceActionCommand = $true
VerifyMetadataHelper $LocalizedData.SkipConflictServiceActionCommandCreation `
$LocalizedData.ConflictServiceActionCommandCreationWithWarning $entitySet.Name `
$currentCommand.Name $metaDataUri $allowClobber $callerPSCmdlet
}
}
if(!$hasConflictingServiceActionCommand -or $allowClobber)
{
$validServiceActions += $currentAction
}
}
$metaData.Actions = $validServiceActions
}
# Update Progress bar.
ProgressBarHelper "Export-ODataEndpointProxy" $progressBarStatus 5 20 1 1
}
#########################################################
# GenerateClientSideProxyModule is a helper function used
# to generate a PowerShell module that serves as a client
# side proxy for interacting with the server side
# OData endpoint. The proxy module conatins proxy cmdlets
# implemneted in CDXML modules and they are exposed
# through module manifest as nested modules.
# One CDXML module is created for each EntitySet
# described in the metadata. Each CDXML module contains
# CRUD & Service Action specific proxy cmdlets targeting
# the underlying EntityType. There is 1:M mapping between
# EntitySet & its underlying EntityTypes (i.e., all
# entities with in the single EntitySet will be of the
# same EntityType but there can be multiple entities
# of the same type with in an EntitySet).
#########################################################
function GenerateClientSideProxyModule
{
param
(
[ODataUtils.Metadata] $metaData,
[string] $metaDataUri,
[string] $uri,
[string] $outputModule,
[string] $createRequestMethod,
[string] $updateRequestMethod,
[string] $cmdletAdapter,
[Hashtable] $resourceNameMapping,
[Hashtable] $customData,
[string] $progressBarStatus,
[System.Management.Automation.PSCmdlet] $callerPSCmdlet
)
# $uri, $outputModule, $metaDataUri, $createRequestMethod, $updateRequestMethod, & $cmdletAdapter is already validated at the cmdlet layer.
if($metaData -eq $null) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateClientSideProxyModule") }
if($callerPSCmdlet -eq $null) { throw ($LocalizedData.ArguementNullError -f "PSCmdlet", "GenerateClientSideProxyModule") }
if($progressBarStatus -eq $null) { throw ($LocalizedData.ArguementNullError -f "ProgressBarStatus", "GenerateClientSideProxyModule") }
# This function performs the following set of tasks
# while creating the client side proxy module:
# 1. If the server side endpoint exposes comlex types,
# the client side proxy complex types are created
# as C# class in ComplexTypeDefinations.psm1
# 2. Creates proxy cmdlets for CRUD opreations.
# 3. Creates proxy cmdlets for Serice action opreations.
# 4. Creates module manifest.
Write-Verbose ($LocalizedData.VerboseSavingModule -f $outputModule)
$typeDefinationFileName = "ComplexTypeDefinitions.psm1"
$complexTypeMapping = GenerateComplexTypeDefination $metaData $metaDataUri $outputModule $typeDefinationFileName $cmdletAdapter $callerPSCmdlet
ProgressBarHelper "Export-ODataEndpointProxy" $progressBarStatus 20 20 1 1
$complexTypeFileDefinationPath = Join-Path -Path $outputModule -ChildPath $typeDefinationFileName
if(Test-Path -Path $complexTypeFileDefinationPath)
{
$proxyFile = New-Object -TypeName System.IO.FileInfo -ArgumentList $complexTypeFileDefinationPath | Get-Item
if($callerPSCmdlet -ne $null)
{
$callerPSCmdlet.WriteObject($proxyFile)
}
}
$currentEntryCount = 0
foreach ($entitySet in $metaData.EntitySets)
{
$currentEntryCount += 1
if ($cmdletAdapter -eq "NetworkControllerAdapter" -And $customData -And $customData.Contains($entitySet.Name) -eq $false)
{
ProgressBarHelper "Export-ODataEndpointProxy" $progressBarStatus 40 20 $metaData.EntitySets.Count $currentEntryCount
continue
}
GenerateCRUDProxyCmdlet $entitySet $metaData $uri $outputModule $createRequestMethod $updateRequestMethod $cmdletAdapter $resourceNameMapping $customData $complexTypeMapping "Export-ODataEndpointProxy" $progressBarStatus 40 20 $metaData.EntitySets.Count $currentEntryCount $callerPSCmdlet
}
GenerateServiceActionProxyCmdlet $metaData $uri "$outputModule\ServiceActions.cdxml" $complexTypeMapping $progressBarStatus $callerPSCmdlet
$moduleDirInfo = [System.IO.DirectoryInfo]::new($outputModule)
$moduleManifestName = $moduleDirInfo.Name + ".psd1"
GenerateModuleManifest $metaData $outputModule\$moduleManifestName @($typeDefinationFileName, 'ServiceActions.cdxml') $resourceNameMapping $progressBarStatus $callerPSCmdlet
}
#########################################################
# GenerateCRUDProxyCmdlet is a helper function used
# to generate Get, Set, New & Remove proxy cmdlet.
# The proxy cmdlet is generated in the CDXML
# compliant format.
#########################################################
function GenerateCRUDProxyCmdlet
{
param
(
[ODataUtils.EntitySet] $entitySet,
[ODataUtils.Metadata] $metaData,
[string] $uri,
[string] $outputModule,
[string] $createRequestMethod,
[string] $UpdateRequestMethod,
[string] $cmdletAdapter,
[Hashtable] $resourceNameMapping,
[Hashtable] $customData,
[Hashtable] $compexTypeMapping,
[string] $progressBarActivityName,
[string] $progressBarStatus,
[double] $previousSegmentWeight,
[double] $currentSegmentWeight,
[int] $totalNumberofEntries,
[int] $currentEntryCount,
[System.Management.Automation.PSCmdlet] $callerPSCmdlet
)
# $uri, $outputModule, $metaDataUri, $createRequestMethod, $updateRequestMethod, & $cmdletAdapter is already validated at the cmdlet layer.
if($entitySet -eq $null) { throw ($LocalizedData.ArguementNullError -f "EntitySet", "GenerateClientSideProxyModule") }
if($metaData -eq $null) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateClientSideProxyModule") }
if($callerPSCmdlet -eq $null) { throw ($LocalizedData.ArguementNullError -f "PSCmdlet", "GenerateClientSideProxyModule") }
if($progressBarStatus -eq $null) { throw ($LocalizedData.ArguementNullError -f "ProgressBarStatus", "GenerateClientSideProxyModule") }
$entitySetName = $entitySet.Name
if(($resourceNameMapping -ne $null) -and
$resourceNameMapping.ContainsKey($entitySetName))
{
$entitySetName = $resourceNameMapping[$entitySetName]
}
else
{
$entitySetName = $entitySet.Type.Name
}
$Path = "$OutputModule\$entitySetName.cdxml"
$xmlWriter = New-Object System.XMl.XmlTextWriter($Path,$Null)
if ($xmlWriter -eq $null)
{
throw ($LocalizedData.XmlWriterInitializationError -f $entitySet.Name)
}
$xmlWriter = SaveCDXMLHeader $xmlWriter $uri $entitySet.Name $entitySetName $cmdletAdapter
# Get the keys depending on whether the url contains variables or not
if ($CmdletAdapter -ne "NetworkControllerAdapter")
{
$keys = (GetAllProperties $entitySet.Type) | Where-Object { $_.IsKey }
}
else
{
$name = $entitySet.Name
$keys = GetKeys $entitySet $customData.$name 'Get'
}
$navigationProperties = GetAllProperties $entitySet.Type -IncludeOnlyNavigationProperties
GenerateGetProxyCmdlet $xmlWriter $metaData $keys $navigationProperties $cmdletAdapter $compexTypeMapping
$nonKeyProperties = (GetAllProperties $entitySet.Type) | ? { -not $_.isKey }
$nullableProperties = $nonKeyProperties | ? { $_.isNullable }
$nonNullableProperties = $nonKeyProperties | ? { -not $_.isNullable }
$xmlWriter.WriteStartElement('StaticCmdlets')
$keyProperties = $keys
# Do operations specifically needed for NetworkController cmdlets
if ($CmdletAdapter -eq "NetworkControllerAdapter")
{
$keyProperties = GetKeys $entitySet $customData.$name 'New'
$additionalProperties = GetNetworkControllerAdditionalProperties $navigationProperties $metaData
$nullableProperties = UpdateNetworkControllerSpecificProperties $nullableProperties $additionalProperties $keyProperties $true
$nonNullableProperties = UpdateNetworkControllerSpecificProperties $nonNullableProperties $additionalProperties $keyProperties $false
}
GenerateNewProxyCmdlet $xmlWriter $metaData $keyProperties $nonNullableProperties $nullableProperties $navigationProperties $cmdletAdapter $compexTypeMapping
if ($CmdletAdapter -ne "NetworkControllerAdapter")
{
GenerateSetProxyCmdlet $xmlWriter $keyProperties $nonKeyProperties $compexTypeMapping
}
if ($CmdletAdapter -eq "NetworkControllerAdapter")
{
$keyProperties = GetKeys $entitySet $customData.$name 'Remove'
}
GenerateRemoveProxyCmdlet $xmlWriter $metaData $keyProperties $navigationProperties $cmdletAdapter $compexTypeMapping
$entityActions = $metaData.Actions | Where-Object { ($_.EntitySet.Namespace -eq $entitySet.Namespace) -and ($_.EntitySet.Name -eq $entitySet.Name) }
if ($entityActions.Length -gt 0)
{
foreach($action in $entityActions)
{
$xmlWriter = GenerateActionProxyCmdlet $xmlWriter $metaData $action $entitySet.Name $true $keys $complexTypeMapping
}
}
$xmlWriter.WriteEndElement()
$xmlWriter.WriteStartElement('CmdletAdapterPrivateData')
$xmlWriter.WriteStartElement('Data')
$xmlWriter.WriteAttributeString('Name', 'EntityTypeName')
$xmlWriter.WriteString("$($entitySet.Type.Namespace).$($entitySet.Type.Name)")
$xmlWriter.WriteEndElement()
$xmlWriter.WriteStartElement('Data')
$xmlWriter.WriteAttributeString('Name', 'EntitySetName')
$xmlWriter.WriteString("$($entitySet.Namespace).$($entitySet.Name)")
$xmlWriter.WriteEndElement()
# Add the customUri to privateData
if ($CmdletAdapter -eq "NetworkControllerAdapter")
{
$xmlWriter.WriteStartElement('Data')
$xmlWriter.WriteAttributeString('Name', "CustomUriSuffix")
$xmlWriter.WriteString($CustomData.$name)
$xmlWriter.WriteEndElement()
}
# Add CreateRequestMethod and UpdateRequestMethod to privateData
$xmlWriter.WriteStartElement('Data')
$xmlWriter.WriteAttributeString('Name', 'CreateRequestMethod')
$xmlWriter.WriteString("$CreateRequestMethod")
$xmlWriter.WriteEndElement()
$xmlWriter.WriteStartElement('Data')
$xmlWriter.WriteAttributeString('Name', 'UpdateRequestMethod')
$xmlWriter.WriteString("$UpdateRequestMethod")
$xmlWriter.WriteEndElement()
$xmlWriter.WriteEndElement()
SaveCDXMLFooter $xmlWriter
ProcessStreamHelper ($LocalizedData.VerboseSavedCDXML -f $($entitySetName), $Path) $progressBarActivityName $progressBarStatus $previousSegmentWeight $currentSegmentWeight $totalNumberofEntries $currentEntryCount $Path $callerPSCmdlet
}
#########################################################
# GenerateGetProxyCmdlet is a helper function used
# to generate Get-* proxy cmdlet. The proxy cmdlet is
# generated in the CDXML compliant format.
#########################################################
function GenerateGetProxyCmdlet
{
param
(
[System.XMl.XmlTextWriter] $xmlWriter,
[ODataUtils.Metadata] $metaData,
[object[]] $keys,
[object[]] $navigationProperties,
[string] $cmdletAdapter,
[Hashtable] $complexTypeMapping
)
# $cmdletAdapter is already validated at the cmdlet layer.
if($xmlWriter -eq $null) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "GenerateGetProxyCmdlet") }
if($metaData -eq $null) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateGetProxyCmdlet") }
$xmlWriter.WriteStartElement('InstanceCmdlets')
$xmlWriter.WriteStartElement('GetCmdletParameters')
$xmlWriter.WriteAttributeString('DefaultCmdletParameterSet', 'Default')
# adding key parameters and association parameters to QueryableProperties, each in a different parameter set
# to be used by GET cmdlet
if (($keys -ne $null -and $keys.Length -gt 0) -or (($navigationProperties -ne $null -and $navigationProperties.Length -gt 0) -and $cmdletAdapter -ne "NetworkControllerAdapter"))
{
$xmlWriter.WriteStartElement('QueryableProperties')
$position = 0
$keys | ? { $_ -ne $null } | % {
$xmlWriter.WriteStartElement('Property')
$xmlWriter.WriteAttributeString('PropertyName', $_.Name)
$xmlWriter.WriteStartElement('Type')
$PSTypeName = Convert-ODataTypeToCLRType $_.TypeName $complexTypeMapping
$xmlWriter.WriteAttributeString('PSType', $PSTypeName)
$xmlWriter.WriteEndElement()
$xmlWriter.WriteStartElement('RegularQuery')
$xmlWriter.WriteStartElement('CmdletParameterMetadata')
$xmlWriter.WriteAttributeString('PSName', $_.Name)
$xmlWriter.WriteAttributeString('CmdletParameterSets', 'Default')
$xmlWriter.WriteAttributeString('IsMandatory', $_.IsMandatory.ToString().ToLower())
$xmlWriter.WriteAttributeString('Position', $position)
if($_.IsMandatory)
{
$xmlWriter.WriteAttributeString('ValueFromPipelineByPropertyName', 'true')
}
$xmlWriter.WriteEndElement()
$xmlWriter.WriteEndElement()
$xmlWriter.WriteEndElement()
$position++
}
# This behaviour is different for NetworkController specific cmdlets.
if ($CmdletAdapter -ne "NetworkControllerAdapter")
{
$navigationProperties | ? { $_ -ne $null } | % {
$associatedType = GetAssociatedType $metaData $_
$associatedEntitySet = GetEntitySetForEntityType $metaData $associatedType
$nvgProperty = $_
(GetAllProperties $associatedType) | ? { $_.IsKey } | % {
$xmlWriter.WriteStartElement('Property')
$xmlWriter.WriteAttributeString('PropertyName', $associatedEntitySet.Name + ':' + $_.Name + ':Key')
$xmlWriter.WriteStartElement('Type')
$PSTypeName = Convert-ODataTypeToCLRType $_.TypeName $complexTypeMapping
$xmlWriter.WriteAttributeString('PSType', $PSTypeName)
$xmlWriter.WriteEndElement()
$xmlWriter.WriteStartElement('RegularQuery')
$xmlWriter.WriteStartElement('CmdletParameterMetadata')
$xmlWriter.WriteAttributeString('PSName', 'Associated' + $nvgProperty.Name + $_.Name)
$xmlWriter.WriteAttributeString('CmdletParameterSets', $nvgProperty.AssociationName)
$xmlWriter.WriteAttributeString('IsMandatory', 'true')
$xmlWriter.WriteAttributeString('ValueFromPipelineByPropertyName', 'true')
$xmlWriter.WriteEndElement()
$xmlWriter.WriteEndElement()
$xmlWriter.WriteEndElement()
}
}
# Add Query Parameters (i.e., Top, Skip, OrderBy, Filter) to the generated Get-* cmdlets.
$queryParameters =
@{
"Filter" = "Edm.String";
"IncludeTotalResponseCount" = "switch";
"OrderBy" = "Edm.String";
"Select" = "Edm.String";
"Skip" = "Edm.Int32";
"Top" = "Edm.Int32";
}
foreach($currentQueryParameter in $queryParameters.Keys)
{
$xmlWriter.WriteStartElement('Property')
$xmlWriter.WriteAttributeString('PropertyName', "QueryOption:" + $currentQueryParameter)
$xmlWriter.WriteStartElement('Type')
$PSTypeName = Convert-ODataTypeToCLRType $queryParameters[$currentQueryParameter]
$xmlWriter.WriteAttributeString('PSType', $PSTypeName)
$xmlWriter.WriteEndElement()
$xmlWriter.WriteStartElement('RegularQuery')
$xmlWriter.WriteStartElement('CmdletParameterMetadata')
$xmlWriter.WriteAttributeString('PSName', $currentQueryParameter)
if($queryParameters[$currentQueryParameter] -eq "Edm.String")
{
$xmlWriter.WriteStartElement('ValidateNotNullOrEmpty')
$xmlWriter.WriteEndElement()
}
if($queryParameters[$currentQueryParameter] -eq "Edm.Int32")
{
$minValue = 1
# For Skip Query parameter we want to support 0 as the
# minimum skip value in order to support client side paging.
if($currentQueryParameter -eq 'Skip')
{
$minValue = 0
}
$xmlWriter.WriteStartElement('ValidateRange')
$xmlWriter.WriteAttributeString('Min', $minValue)
$xmlWriter.WriteAttributeString('Max', [int]::MaxValue)
$xmlWriter.WriteEndElement()
}
$xmlWriter.WriteEndElement()
$xmlWriter.WriteEndElement()
$xmlWriter.WriteEndElement()
}
}
$xmlWriter.WriteEndElement()
}
$xmlWriter.WriteEndElement()
$xmlWriter.WriteStartElement('GetCmdlet')
$xmlWriter.WriteStartElement('CmdletMetadata')
$xmlWriter.WriteAttributeString('Verb', 'Get')
$xmlWriter.WriteEndElement()
$xmlWriter.WriteEndElement()
$xmlWriter.WriteEndElement()
}
#########################################################
# GenerateNewProxyCmdlet is a helper function used
# to generate New-* proxy cmdlet. The proxy cmdlet is
# generated in the CDXML compliant format.
#########################################################
function GenerateNewProxyCmdlet
{
param
(
[System.XMl.XmlTextWriter] $xmlWriter,
[ODataUtils.Metadata] $metaData,
[object[]] $keyProperties,
[object[]] $nonNullableProperties,
[object[]] $nullableProperties,
[object[]] $navigationProperties,
[string] $cmdletAdapter,
[Hashtable] $complexTypeMapping
)
# $cmdletAdapter is already validated at the cmdlet layer.
if($xmlWriter -eq $null) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "GenerateNewProxyCmdlet") }
if($metaData -eq $null) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateNewProxyCmdlet") }
$xmlWriter.WriteStartElement('Cmdlet')
$xmlWriter.WriteStartElement('CmdletMetadata')
$xmlWriter.WriteAttributeString('Verb', 'New')
$xmlWriter.WriteAttributeString('DefaultCmdletParameterSet', 'Default')
$xmlWriter.WriteAttributeString('ConfirmImpact', 'Medium')
$xmlWriter.WriteEndElement()
$xmlWriter.WriteStartElement('Method')
$xmlWriter.WriteAttributeString('MethodName', 'Create')
$xmlWriter.WriteAttributeString('CmdletParameterSet', 'Default')
AddParametersNode $xmlWriter $keyProperties $nonNullableProperties $nullableProperties $null $true $true $complexTypeMapping
$xmlWriter.WriteEndElement()
# This behaviour is different for NetworkControllerCmdlets
if ($CmdletAdapter -ne "NetworkControllerAdapter")
{
$navigationProperties | ? { $_ -ne $null } | % {
$associatedType = GetAssociatedType $metaData $_
$associatedEntitySet = GetEntitySetForEntityType $metaData $associatedType
$xmlWriter.WriteStartElement('Method')
$xmlWriter.WriteAttributeString('MethodName', "Association:Create:$($associatedEntitySet.Name)")
$xmlWriter.WriteAttributeString('CmdletParameterSet', $_.Name)
$associatedKeys = ((GetAllProperties $associatedType) | ? { $_.isKey })
AddParametersNode $xmlWriter $associatedKeys $keyProperties $null "Associated$($_.Name)" $true $true $complexTypeMapping
$xmlWriter.WriteEndElement()
}
}
$xmlWriter.WriteEndElement()
}
#########################################################
# GenerateRemoveProxyCmdlet is a helper function used
# to generate Remove-* proxy cmdlet. The proxy cmdlet is
# generated in the CDXML compliant format.
#########################################################
function GenerateRemoveProxyCmdlet
{
param
(
[System.XMl.XmlTextWriter] $xmlWriter,
[ODataUtils.Metadata] $metaData,
[object[]] $keyProperties,
[object[]] $navigationProperties,
[string] $cmdletAdapter,
[Hashtable] $complexTypeMapping
)
# $metaData, $cmdletAdapter & $cmdletAdapter are already validated at the cmdlet layer.
if($xmlWriter -eq $null) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "GenerateRemoveProxyCmdlet") }
if($metaData -eq $null) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateRemoveProxyCmdlet") }
$xmlWriter.WriteStartElement('Cmdlet')
$xmlWriter.WriteStartElement('CmdletMetadata')
$xmlWriter.WriteAttributeString('Verb', 'Remove')
$xmlWriter.WriteAttributeString('DefaultCmdletParameterSet', 'Default')
$xmlWriter.WriteAttributeString('ConfirmImpact', 'Medium')
$xmlWriter.WriteEndElement()
$xmlWriter.WriteStartElement('Method')
$xmlWriter.WriteAttributeString('MethodName', 'Delete')
$xmlWriter.WriteAttributeString('CmdletParameterSet', 'Default')
# This behaviour is different for NetworkControllerCmdlets
if ($CmdletAdapter -eq "NetworkControllerAdapter")
{
# Add etag for NetworkControllerCmdlets
$otherProperties = @([ODataUtils.TypeProperty] @{
"Name" = "Etag";
"TypeName" = "Edm.String";
"IsNullable" = $true;
})
AddParametersNode $xmlWriter $keyProperties $null $otherProperties $null $true $true $complexTypeMapping
}
else
{
AddParametersNode $xmlWriter $keyProperties $null $null $null $true $true $complexTypeMapping
}
$xmlWriter.WriteEndElement()
# This behaviour is different for NetworkControllerCmdlets
if ($CmdletAdapter -ne "NetworkControllerAdapter")
{
$navigationProperties | ? { $_ -ne $null } | % {
$associatedType = GetAssociatedType $metaData $_
$associatedEntitySet = GetEntitySetForEntityType $metaData $associatedType
$xmlWriter.WriteStartElement('Method')
$xmlWriter.WriteAttributeString('MethodName', "Association:Delete:$($associatedEntitySet.Name)")
$xmlWriter.WriteAttributeString('CmdletParameterSet', $_.Name)
$associatedType = GetAssociatedType $metaData $_
$associatedKeys = ((GetAllProperties $associatedType) | ? { $_.isKey })
AddParametersNode $xmlWriter $associatedKeys $keyProperties $null "Associated$($_.Name)" $true $true $complexTypeMapping
$xmlWriter.WriteEndElement()
}
}
$xmlWriter.WriteEndElement()
}
#########################################################
# GenerateActionProxyCmdlet is a helper function used
# to generate Invoke-* proxy cmdlet. These proxy cmdlets
# support Instance/Service level actions. They are
# generated in the CDXML compliant format.
#########################################################
function GenerateActionProxyCmdlet
{
param
(
[System.Xml.XmlWriter] $xmlWriter,
[ODataUtils.Metadata] $metaData,
[ODataUtils.Action] $action,
[string] $noun,
[bool] $isInstanceAction,
[ODataUtils.TypeProperty] $keys,
[Hashtable] $complexTypeMapping
)
# $metaData is already validated at the cmdlet layer.
if($xmlWriter -eq $null) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "GenerateActionProxyCmdlet") }
if($metaData -eq $null) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateActionProxyCmdlet") }
if($action -eq $null) { throw ($LocalizedData.ArguementNullError -f "Action", "GenerateActionProxyCmdlet") }
if($noun -eq $null) { throw ($LocalizedData.ArguementNullError -f "Noun", "GenerateActionProxyCmdlet") }
$xmlWriter.WriteStartElement('Cmdlet')
$xmlWriter.WriteStartElement('CmdletMetadata')
$xmlWriter.WriteAttributeString('Verb', 'Invoke')
$xmlWriter.WriteAttributeString('Noun', "$($noun)$($action.Verb)")
$xmlWriter.WriteAttributeString('ConfirmImpact', 'Medium')
$xmlWriter.WriteEndElement()
$xmlWriter.WriteStartElement('Method')
$xmlWriter.WriteAttributeString('MethodName', "Action:$($action.Verb):$($action.EntitySet.Name)")
$xmlWriter.WriteStartElement('Parameters')
$keys | ? { $_ -ne $null } | % {
$xmlWriter.WriteStartElement('Parameter')
$xmlWriter.WriteAttributeString('ParameterName', $_.Name + ':Key')
$xmlWriter.WriteStartElement('Type')
$PSTypeName = Convert-ODataTypeToCLRType $_.TypeName $complexTypeMapping
$xmlWriter.WriteAttributeString('PSType', $PSTypeName)
$xmlWriter.WriteEndElement()
$xmlWriter.WriteStartElement('CmdletParameterMetadata')
$xmlWriter.WriteAttributeString('PSName', $_.Name)
$xmlWriter.WriteAttributeString('IsMandatory', 'true')
$xmlWriter.WriteAttributeString('ValueFromPipelineByPropertyName', 'true')
$xmlWriter.WriteEndElement()
$xmlWriter.WriteEndElement()
}
$i = -1
foreach ($parameter in $action.Parameters)
{
$i++
# for Instance actions, first parameter is Entity Set which we refer to using keys
if ($isInstanceAction -and ($i -eq 0))
{
continue
}
$xmlWriter.WriteStartElement('Parameter')
$xmlWriter.WriteAttributeString('ParameterName', $parameter.Name)
$xmlWriter.WriteStartElement('Type')
$PSTypeName = Convert-ODataTypeToCLRType $parameter.TypeName
$xmlWriter.WriteAttributeString('PSType', $PSTypeName)
$xmlWriter.WriteEndElement()
$xmlWriter.WriteStartElement('CmdletParameterMetadata')
$xmlWriter.WriteAttributeString('PSName', $parameter.Name)
if (-not $parameter.IsNullable)
{
$xmlWriter.WriteAttributeString('IsMandatory', 'true')
$xmlWriter.WriteAttributeString('ValueFromPipelineByPropertyName', 'true')
}
$xmlWriter.WriteEndElement()
$xmlWriter.WriteEndElement()
}
# Add -Force parameter to Service Action cmdlets.
AddParametersNode $xmlWriter $null $null $null $null $true $false $complexTypeMapping
$xmlWriter.WriteEndElement()
$xmlWriter.WriteEndElement()
$xmlWriter.WriteEndElement()
$xmlWriter
}
#########################################################
# GenerateServiceActionProxyCmdlet is a helper function
# used to generate Invoke-* proxy cmdlet. These proxy
# cmdlets support all Service-level actions. They are
# generated in the CDXML compliant format.
#########################################################
function GenerateServiceActionProxyCmdlet
{
param
(
[Parameter(Mandatory=$true)]
[ODataUtils.Metadata] $metaData,
[Parameter(Mandatory=$true)]
[string] $uri,
[Parameter(Mandatory=$true)]
[string] $path,
[Hashtable] $complexTypeMapping,
[string] $progressBarStatus,
[System.Management.Automation.PSCmdlet] $callerPSCmdlet
)
# $uri is already validated at the cmdlet layer.
if($metaData -eq $null) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateServiceActionProxyCmdlet") }
$xmlWriter = New-Object System.XMl.XmlTextWriter($path,$Null)
if ($xmlWriter -eq $null)
{
throw $LocalizedData.XmlWriterInitializationError -f "ServiceActions"
}
$xmlWriter = SaveCDXMLHeader $xmlWriter $uri 'ServiceActions' 'ServiceActions'
$actions = $metaData.Actions | Where-Object { $_.EntitySet -eq $null }
if ($actions.Length -gt 0)
{
$xmlWriter.WriteStartElement('StaticCmdlets')
foreach ($action in $actions)
{
$xmlWriter = GenerateActionProxyCmdlet $xmlWriter $metaData $action '' $false $null $complexTypeMapping
}
$xmlWriter.WriteEndElement()
}
$xmlWriter.WriteStartElement('CmdletAdapterPrivateData')
$xmlWriter.WriteStartElement('Data')
$xmlWriter.WriteAttributeString('Name', 'Namespace')
$xmlWriter.WriteString("$($EntitySet.Namespace)")
$xmlWriter.WriteEndElement()
$xmlWriter.WriteEndElement()
SaveCDXMLFooter $xmlWriter
ProcessStreamHelper ($LocalizedData.VerboseSavedServiceActions -f $path) "Export-ODataEndpointProxy" $progressBarStatus 60 20 1 1 $path $callerPSCmdlet
}
#########################################################
# GenerateModuleManifest is a helper function used
# to generate a wrapper module manifest file. The
# generated module manifest is persisted to the disk at
# the specified OutPutModule path. When the module
# manifest is imported, the following comands will
# be imported:
# 1. Get, Set, New & Remove proxy cmdlets.
# 2. If the server side Odata endpoint exposes complex
# types, then the corresponding client side proxy
# complex types imported.
# 3. Service Action proxy cmdlets.
#########################################################
function GenerateModuleManifest
{
param
(
[ODataUtils.Metadata] $metaData,
[String] $modulePath,
[string[]] $additionalModules,
[Hashtable] $resourceNameMapping,
[string] $progressBarStatus,
[System.Management.Automation.PSCmdlet] $callerPSCmdlet
)
if($metaData -eq $null) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateModuleManifest") }
if($modulePath -eq $null) { throw ($LocalizedData.ArguementNullError -f "ModulePath", "GenerateModuleManifest") }
if($progressBarStatus -eq $null) { throw ($LocalizedData.ArguementNullError -f "ProgressBarStatus", "GenerateModuleManifest") }
$NestedModules = @()
foreach ($entitySet in $metaData.EntitySets)
{
$entitySetName = $entitySet.Name
if(($resourceNameMapping -ne $null) -and
$resourceNameMapping.ContainsKey($entitySetName))
{
$entitySetName = $resourceNameMapping[$entitySetName]
}
else
{
$entitySetName = $entitySet.Type.Name
}
$NestedModules += "$OutputModule\$($entitySetName).cdxml"
}
New-ModuleManifest -Path $modulePath -NestedModules ($AdditionalModules + $NestedModules)
ProcessStreamHelper ($LocalizedData.VerboseSavedModuleManifest -f $modulePath) "Export-ODataEndpointProxy" $progressBarStatus 80 20 1 1 $modulePath $callerPSCmdlet
}
#########################################################
# GetBaseType is a helper function used to fetch the
# base type of the given type.
#########################################################
function GetBaseType
{
param
(
[System.Xml.XmlElement] $metadataEntityDefinition,
[ODataUtils.Metadata] $metaData
)
if ($metadataEntityDefinition -ne $null -and
$metaData -ne $null -and
$metadataEntityDefinition.BaseType -ne $null)
{
$baseType = $metaData.EntityTypes | Where {$_.Namespace+"."+$_.Name -eq $metadataEntityDefinition.BaseType}
if ($baseType -eq $null)
{
$baseType = $metaData.ComplexTypes | Where {$_.Namespace+"."+$_.Name -eq $metadataEntityDefinition.BaseType}
}
}
if ($baseType -ne $null)
{
$baseType[0]
}
}
#########################################################
# AddDerivedTypes is a helper function used to process
# derived types of a newly added type, that were
# previously waiting in the queue.
#########################################################
function AddDerivedTypes
{
param
(
[ODataUtils.EntityType] $baseType,
[Hashtable]$entityAndComplexTypesQueue,
[ODataUtils.Metadata] $metaData,
[string] $namespace
)
# $metaData is already validated at the cmdlet layer.
if($baseType -eq $null) { throw ($LocalizedData.ArguementNullError -f "BaseType", "AddDerivedTypes") }
if($entityAndComplexTypesQueue -eq $null) { throw ($LocalizedData.ArguementNullError -f "EntityAndComplexTypesQueue", "AddDerivedTypes") }
if($namespace -eq $null) { throw ($LocalizedData.ArguementNullError -f "Namespace", "AddDerivedTypes") }
$baseTypeFullName = $baseType.Namespace + '.' + $baseType.Name
if ($entityAndComplexTypesQueue.ContainsKey($baseTypeFullName))
{
foreach ($type in $entityAndComplexTypesQueue[$baseTypeFullName])
{
if ($type.type -eq 'EntityType')
{
$newType = ParseMetadataTypeDefinition ($type.value) $baseType $metaData $namespace $true
$metaData.EntityTypes += $newType
}
else
{
$newType = ParseMetadataTypeDefinition ($type.value) $baseType $metaData $namespace $false
$metaData.ComplexTypes += $newType
}
AddDerivedTypes $newType $entityAndComplexTypesQueue $metaData $namespace
}
}
}
#########################################################
# ParseMetadataTypeDefinition is a helper function used
# to parse types definitions element of metadata xml.
#########################################################
function ParseMetadataTypeDefinition
{
param
(
[Parameter(Mandatory=$true)]
[System.Xml.XmlElement] $metadataEntityDefinition,
[ODataUtils.EntityType] $baseType,
[ODataUtils.Metadata] $metaData,
[string] $namespace,
[bool] $isEntity
)
# $metaData is already validated at the cmdlet layer.
if($metadataEntityDefinition -eq $null) { throw ($LocalizedData.ArguementNullError -f "MetadataEntityDefinition", "ParseMetadataTypeDefinition") }
if($namespace -eq $null) { throw ($LocalizedData.ArguementNullError -f "Namespace", "ParseMetadataTypeDefinition") }
$newEntityType = [ODataUtils.EntityType] @{
"Namespace" = $namespace;
"Name" = $metadataEntityDefinition.Name;
"IsEntity" = $isEntity;
"BaseType" = $baseType;
}
# properties defined on EntityType
$newEntityType.EntityProperties = $metadataEntityDefinition.Property | % {
if ($_ -ne $null)
{
if ($_.Nullable -ne $null)
{
$newPropertyIsNullable = [System.Convert]::ToBoolean($_.Nullable)
}
else
{
$newPropertyIsNullable = $true
}
[ODataUtils.TypeProperty] @{
"Name" = $_.Name;
"TypeName" = $_.Type;
"IsNullable" = $newPropertyIsNullable;
}
}
}
# navigation properties defined on EntityType
$newEntityType.NavigationProperties = $metadataEntityDefinition.NavigationProperty | % {
if ($_ -ne $null)
{
($AssociationNamespace, $AssociationName) = SplitNamespaceAndName $_.Relationship
[ODataUtils.NavigationProperty] @{
"Name" = $_.Name;
"FromRole" = $_.FromRole;
"ToRole" = $_.ToRole;
"AssociationNamespace" = $AssociationNamespace;
"AssociationName" = $AssociationName;
}
}
}
foreach ($entityTypeKey in $metadataEntityDefinition.Key.PropertyRef)
{
((GetAllProperties $newEntityType) | Where-Object { $_.Name -eq $entityTypeKey.Name }).IsKey = $true
}
$newEntityType
}
#########################################################
# GetAllProperties is a helper function used to fetch
# the entity properties or navigation properties of
# the entity type as well as that of complete base
# type hierarchy.
#########################################################
function GetAllProperties
{
param
(
[ODataUtils.EntityType] $entityType,
[switch] $IncludeOnlyNavigationProperties
)
if($entityType -eq $null) { throw ($LocalizedData.ArguementNullError -f "EntityType", "GetAllProperties") }
$requestedProperties = @()
# Populate EntityType property from current EntityType as well
# as from the corresponding base types recursively if
# $IncludeOnlyNavigationProperties switch parameter is used then follow
# the same routine for navigation properties.
$currentEntityType = $entityType
while($currentEntityType -ne $null)
{
if($IncludeOnlyNavigationProperties.IsPresent)
{
$chosenProperties = $currentEntityType.NavigationProperties
}
else
{
$chosenProperties = $currentEntityType.EntityProperties
}
$requestedProperties += $chosenProperties
$currentEntityType = $currentEntityType.BaseType
}
return $requestedProperties
}
#########################################################
# SplitNamespaceAndName is a helper function used
# to split Namespace and actual Name.
# e.g. "a.b.c" is namespace "a.b" and name "c"
#########################################################
function SplitNamespaceAndName
{
param
(
[string] $fullyQualifiedName
)
if($fullyQualifiedName -eq $null) { throw ($LocalizedData.ArguementNullError -f "FUllyQualifiedName", "SplitNamespaceAndName") }
$sa = $fullyQualifiedName -split "(.*)\.(.*)"
if ($sa.Length -gt 1)
{
# return Namespace
$sa[1]
# return Name
$sa[2]
}
else
{
# return Namespace
""
# return Name
$sa[0]
}
}
#########################################################
# GetEntitySetForEntityType is a helper function used
# to fetch EntitySet for a given EntityType by
# searching the inheritance hierarchy in the
# supplied metadata.
#########################################################
function GetEntitySetForEntityType
{
param
(
[ODataUtils.Metadata] $metaData,
[ODataUtils.EntityType] $entityType
)
# $metaData is already validated at the cmdlet layer.
if($entityType -eq $null) { throw ($LocalizedData.ArguementNullError -f "EntityType", "GetEntitySetForEntityType") }
$result = $metaData.EntitySets | ? { ($_.Type.Namespace -eq $entityType.Namespace) -and ($_.Type.Name -eq $entityType.Name) }
if (($result.Count -eq 0) -and ($entityType.BaseType -ne $null))
{
GetEntitySetForEntityType $metaData $entityType.BaseType
}
elseif ($result.Count -gt 1)
{
throw ($LocalizedData.WrongCountEntitySet -f (($entityType.Namespace + "." + $entityType.Name), $result.Count))
}
$result
}
#########################################################
# ProcessStreamHelper is a helper function that performs
# the following utility tasks:
# 1. Writes verobose messsages to the stream.
# 2. Writes FileInfo objects for the proxy modules
# saved to the disk. This is done to keep the user
# experience in consistent with Export-PSSession.
# 3. Updates progess bar.
#########################################################
function ProcessStreamHelper
{
param
(
[string] $verboseMessage,
[string] $progressBarActivityName,
[string] $status,
[double] $previousSegmentWeight,
[double] $currentSegmentWeight,
[int] $totalNumberofEntries,
[int] $currentEntryCount,
[string] $path,
[System.Management.Automation.PSCmdlet] $callerPSCmdlet
)
Write-Verbose -Message $verboseMessage
ProgressBarHelper $progressBarActivityName $status $previousSegmentWeight $currentSegmentWeight $totalNumberofEntries $currentEntryCount
$proxyFile = New-Object -TypeName System.IO.FileInfo -ArgumentList $path | Get-Item
if($callerPSCmdlet -ne $null)
{
$callerPSCmdlet.WriteObject($proxyFile)
}
}
#########################################################
# GetAssociatedType is a helper function used
# to fetch associated instance's EntityType
# for a given Navigation property in the
# supplied metadata.
#########################################################
function GetAssociatedType
{
param
(
[ODataUtils.Metadata] $Metadata,
[ODataUtils.NavigationProperty] $navProperty
)
# $metaData is already validated at the cmdlet layer.
if($navProperty -eq $null) { throw ($LocalizedData.ArguementNullError -f "NavigationProperty", "GetAssociatedType") }
$associationName = $navProperty.AssociationName
$association = $Metadata.Associations | ? { $_.Name -eq $associationName }
$associationType = $association.Type
if ($associationType.Count -lt 1)
{
throw ($LocalizedData.AssociationNotFound -f $associationName)
}
elseif ($associationType.Count -gt 1)
{
throw ($LocalizedData.TooManyMatchingAssociationTypes -f $associationType.Count, $associationName)
}
if ($associationType.NavPropertyName1 -eq $navProperty.ToRole)
{
$associatedType = $associationType.EndType1
}
elseif ($associationType.NavPropertyName2 -eq $navProperty.ToRole)
{
$associatedType = $associationType.EndType2
}
else
{
throw ($LocalizedData.ZeroMatchingAssociationTypes -f $navProperty.ToRole, $association.Name)
}
# return associated EntityType
$associatedType
}
#########################################################
# AddParametersNode is a helper function used
# to add parameters to the generated proxy cmdlet,
# based on mandatoryProperties and otherProperties.
# PrefixForKeys is used by associations to append a
# prefix to PowerShell parameter name.
#########################################################
function AddParametersNode
{
param
(
[Parameter(Mandatory=$true)]
[System.Xml.XmlWriter] $xmlWriter,
[ODataUtils.TypeProperty[]] $keyProperties,
[ODataUtils.TypeProperty[]] $mandatoryProperties,
[ODataUtils.TypeProperty[]] $otherProperties,
[string] $prefixForKeys,
[boolean] $addForceParameter,
[boolean] $addParametersElement,
[Hashtable] $complexTypeMapping
)
if($xmlWriter -eq $null) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "AddParametersNode") }
if(($keyProperties.Length -gt 0) -or
($mandatoryProperties.Length -gt 0) -or
($otherProperties.Length -gt 0) -or
($addForceParameter))
{
if($addParametersElement)
{
$xmlWriter.WriteStartElement('Parameters')
}
$pos = 0
if ($keyProperties -ne $null)
{
$pos = AddParametersCDXML $xmlWriter $keyProperties $pos $true $prefixForKeys ":Key" $complexTypeMapping
}
if ($mandatoryProperties -ne $null)
{
$pos = AddParametersCDXML $xmlWriter $mandatoryProperties $pos $true $null $null $complexTypeMapping
}
if ($otherProperties -ne $null)
{
$pos = AddParametersCDXML $xmlWriter $otherProperties $pos $false $null $null $complexTypeMapping
}
if($addForceParameter)
{
$forceParameter = [ODataUtils.TypeProperty] @{
"Name" = "Force";
"TypeName" = "switch";
"IsNullable" = $false
}
$pos = AddParametersCDXML $xmlWriter $forceParameter $pos $false $null $null $complexTypeMapping
}
if($addParametersElement)
{
$xmlWriter.WriteEndElement()
}
}
}
#########################################################
# AddParametersNode is a helper function used
# to add Parameter node to CDXML based on properties.
# Prefix is appended to PS parameter names, used for
# associations. Suffix is appended to all parameter
# names, for ex. to differentiate keys. returns new $pos
#########################################################
function AddParametersCDXML
{
param
(
[Parameter(Mandatory=$true)]
[System.Xml.XmlWriter] $xmlWriter,
[ODataUtils.TypeProperty[]] $properties,
[Parameter(Mandatory=$true)]
[int] $pos,
[bool] $isMandatory,
[string] $prefix,
[string] $suffix,
[Hashtable] $compleTypeMapping
)
$properties | ? { $_ -ne $null } | % {
$xmlWriter.WriteStartElement('Parameter')
$xmlWriter.WriteAttributeString('ParameterName', $_.Name + $suffix)
$xmlWriter.WriteStartElement('Type')
$PSTypeName = Convert-ODataTypeToCLRType $_.TypeName $compleTypeMapping
$xmlWriter.WriteAttributeString('PSType', $PSTypeName)
$xmlWriter.WriteEndElement()
$xmlWriter.WriteStartElement('CmdletParameterMetadata')
$xmlWriter.WriteAttributeString('PSName', $prefix + $_.Name)
$xmlWriter.WriteAttributeString('IsMandatory', ($isMandatory).ToString().ToLowerInvariant())
$xmlWriter.WriteAttributeString('Position', $pos)
if($isMandatory)
{
$xmlWriter.WriteAttributeString('ValueFromPipelineByPropertyName', 'true')
}
$xmlWriter.WriteEndElement()
$xmlWriter.WriteEndElement()
$pos++
}
$pos
}
#########################################################
# GenerateComplexTypeDefination is a helper function used
# to generate comlplex type defination from the metadata.
#########################################################
function GenerateComplexTypeDefination
{
param
(
[ODataUtils.Metadata] $metaData,
[string] $metaDataUri,
[string] $OutputModule,
[string] $typeDefinationFileName,
[string] $cmdletAdapter,
[System.Management.Automation.PSCmdlet] $callerPSCmdlet
)
#metadataUri, $OutputModule & $cmdletAdapter are already validated at the cmdlet layer.
if($typeDefinationFileName -eq $null) { throw ($LocalizedData.ArguementNullError -f "TypeDefinationFileName", "GenerateComplexTypeDefination") }
if($metaData -eq $null) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateComplexTypeDefination") }
if($callerPSCmdlet -eq $null) { throw ($LocalizedData.ArguementNullError -f "PSCmdlet", "GenerateComplexTypeDefination") }
$Path = "$OutputModule\$typeDefinationFileName"
# We are currently generating classes for EntityType & ComplexType
# defination exposed in the metadata.
$typesToBeGenerated = $metaData.EntityTypes+$metadata.ComplexTypes
if($typesToBeGenerated -ne $null -and $typesToBeGenerated.Count -gt 0)
{
$complexTypeMapping = @{}
$entityTypeNameSpaceMapping = @{}
foreach ($entityType in $typesToBeGenerated)
{
if ($entityType -ne $null)
{
$entityTypeFullName = $entityType.Namespace + '.' + $entityType.Name
if(!$complexTypeMapping.ContainsKey($entityTypeFullName))
{
$complexTypeMapping.Add($entityTypeFullName, $entityType.Name)
}
if(!$entityTypeNameSpaceMapping.ContainsKey($entityType.Namespace))
{
$entityTypes = @()
$entityTypeNameSpaceMapping.Add($entityType.Namespace, $entityTypes)
}
$entityTypeNameSpaceMapping[$entityType.Namespace] += $entityType
}
}
if($entityTypeNameSpaceMapping.Count -gt 0)
{
$output = @"
`$typeDefinitions = @"
using System;
using System.Management.Automation;
"@
foreach($currentNameSpace in $entityTypeNameSpaceMapping.Keys)
{
$entityTypes = $entityTypeNameSpaceMapping[$currentNameSpace]
$output += "`r`nnamespace $(ValidateComplexTypeIdentifier $currentNameSpace $true $metaDataUri $callerPSCmdlet)`r`n{"
foreach ($entityType in $entityTypes)
{
$entityTypeFullName = (ValidateComplexTypeIdentifier $entityType.Namespace $true $metaDataUri $callerPSCmdlet) + '.' + $entityType.Name
Write-Verbose ($LocalizedData.VerboseAddingTypeDefinationToGeneratedModule -f $entityTypeFullName, "$OutputModule\$typeDefinationFileName")
if($entityType.BaseType -ne $null)
{
$entityBaseFullName = (ValidateComplexTypeIdentifier $entityType.BaseType.Namespace $true $metaDataUri $callerPSCmdlet) + '.' + (ValidateComplexTypeIdentifier $entityType.BaseType.Name $false $metaDataUri $callerPSCmdlet)
$output += "`r`n public class $(ValidateComplexTypeIdentifier $entityType.Name $false $metaDataUri $callerPSCmdlet) : $($entityBaseFullName)`r`n {"
}
else
{
$output += "`r`n public class $(ValidateComplexTypeIdentifier $entityType.Name $false $metaDataUri $callerPSCmdlet)`r`n {"
}
$properties = $null
for($index = 0; $index -lt $entityType.EntityProperties.Count; $index++)
{
$property = $entityType.EntityProperties[$index]
$typeName = Convert-ODataTypeToCLRType $property.TypeName $complexTypeMapping
$properties += "`r`n public $typeName $(ValidateComplexTypeIdentifier $property.Name $false $metaDataUri $callerPSCmdlet);"
}
# Navigation properties are treated like any other property for NetworkController scenario.
if ($cmdletAdapter -eq "NetworkControllerAdapter")
{
for($index = 0; $index -lt $entityType.NavigationProperties.Count; $index++)
{
$property = $entityType.NavigationProperties[$index]
$navigationTypeName = GetNavigationPropertyTypeName $property $metaData
$typeName = Convert-ODataTypeToCLRType $navigationTypeName $complexTypeMapping
$properties += "`r`n public $typeName $(ValidateComplexTypeIdentifier $property.Name $false $metaDataUri $callerPSCmdlet);"
}
}
$output += $properties
$output += "`r`n }`r`n"
}
$output += "}`r`n"
}
$output += """@`r`n"
$output += "Add-Type -TypeDefinition `$typeDefinitions `r`n"
$output | Out-File -FilePath $Path
Write-Verbose ($LocalizedData.VerboseSavedTypeDefinationModule -f $typeDefinationFileName, $OutputModule)
}
}
return $complexTypeMapping
}
# Creating a single instace of CSharpCodeProvider that would be used
# for Identifier validation in the ValidateComplexTypeIdentifier helper method.
$cSharpCodeProvider = [Microsoft.CSharp.CSharpCodeProvider]::new()
#########################################################
# ValidateComplexTypeIdentifier is a helper function to
# make sure that the type names defined in the
# metadata are valid C# Identifier names. This validation
# is performed to make sure that there are no security
# threat from importing the generated complex type
# (which is created using the metadata file).
# This method return the identifier name if its a
# valid identifier, else a terminating error in thrown.
#########################################################
function ValidateComplexTypeIdentifier
{
param
(
[string] $identifierName,
[bool] $isNameSpaceName,
[string] $metaDataUri,
[System.Management.Automation.PSCmdlet] $callerPSCmdlet
)
if($callerPSCmdlet -eq $null) { throw ($LocalizedData.ArguementNullError -f "PSCmdlet", "ValidateComplexTypeIdentifier") }
if($isNameSpaceName)
{
$independentIdentifiers = $identifierName.Split('.')
$result = $true
foreach($currentIdentifier in $independentIdentifiers)
{
if(![System.CodeDom.Compiler.CodeGenerator]::IsValidLanguageIndependentIdentifier($currentIdentifier))
{
$result = $false
break
}
}
}
else
{
$result = $cSharpCodeProvider.IsValidIdentifier($identifierName)
}
if(!$result)
{
$errorMessage = ($LocalizedData.InValidIdentifierInMetadata -f $metaDataUri, $identifierName)
$errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidIdentifier" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidData) $null $identifierName
$callerPSCmdlet.ThrowTerminatingError($errorRecord)
}
else
{
return $identifierName
}
}
#########################################################
# GetKeys is a helper function used to
# return the keys for the entity if customUri
# is specified.
#########################################################
function GetKeys
{
param
(
[ODataUtils.EntitySet] $entitySet,
[string] $customUri,
[string] $actionName
)
# Get the original keys
$key = (GetAllProperties $entitySet.Type) | Where-Object { $_.IsKey }
# Get the keys with delimiters
$keys = $customUri -split "/" | % {
if ($_ -match '{*}')
{
[ODataUtils.TypeProperty] @{
"Name" = $_.Substring($_.IndexOf('{')+1,$_.IndexOf('}')-$_.IndexOf('{')-1);
"TypeName" = "Edm.String";
"IsNullable" = $false;
"IsMandatory" = $true;
}
}
elseif ($_ -match '\[*\]')
{
if ($ActionName -eq 'Get') {
[ODataUtils.TypeProperty] @{
"Name" = $_.Substring($_.IndexOf('[')+1,$_.IndexOf(']')-$_.IndexOf('[')-1);
"TypeName" = "Edm.String";
"IsNullable" = $false;
"IsMandatory" = $false;
}
}
else {
[ODataUtils.TypeProperty] @{
"Name" = $_.Substring($_.IndexOf('[')+1,$_.IndexOf(']')-$_.IndexOf('[')-1);
"TypeName" = "Edm.String";
"IsNullable" = $false;
"IsMandatory" = $true;
}
}
}
}
# Now combine the two keys and avoid duplication
# Make a list of names already present in the new keys
# Foreach old key check if that key is present in the new keyList
# Else add the key to new key list
$keyParams = $keys | ForEach-Object {$_.Name}
if ($keyParams -eq $null -Or $keyParams.Count -eq 0) {
$keys = $key
}
else {
if ($keyParams.Count -eq 1) {
$keys = @($keys)
}
$key | ForEach-Object {
if ($keyParams.Contains($_.Name) -eq $false)
{
$keys += $_
}
}
}
$keys
}
#########################################################
# GetNetworkControllerAdditionalProperties is a helper
# function used to fetch network controller specific
# additional properties.
#########################################################
function GetNetworkControllerAdditionalProperties
{
param
(
$navigationProperties,
$metaData
)
# Additional properties contains the types present as navigation properties
$additionalProperties = $navigationProperties | ? { $_ -ne $null } | %{
$typeName = GetNavigationPropertyTypeName $_ $metaData
if ($_.Name -eq "Properties") {
$isNullable = $false
}
else {
$isNullable = $true
}
[ODataUtils.TypeProperty] @{
"Name" = $_.Name;
"TypeName" = $typeName
"IsNullable" = $isNullable;
}
}
# Add etag to the additionalProperties
if ($additionalProperties -ne $null)
{
if ($additionalProperties.Count -eq 1) {
$additionalProperties = @($additionalProperties)
}
$additionalProperties += [ODataUtils.TypeProperty] @{
"Name" = "Etag";
"TypeName" = "Edm.String";
"IsNullable" = $true;
}
}
else
{
$additionalProperties = [ODataUtils.TypeProperty] @{
"Name" = "Etag";
"TypeName" = "Edm.String";
"IsNullable" = $true;
}
}
$additionalProperties
}
#########################################################
# UpdateNetworkControllerSpecificProperties is a
# helper function used to append additionalProperties
# to nullable/nonNullable Properties. This is network controller
# specific logic.
#########################################################
function UpdateNetworkControllerSpecificProperties
{
param
(
$nullableProperties,
$additionalProperties,
$keyProperties,
$isNullable
)
if ($isNullable) {
$additionalProperties = $additionalProperties | ? { $_.isNullable }
}
else {
$additionalProperties = $additionalProperties | ? { -not $_.isNullable }
}
if ($nullableProperties -eq $null)
{
$nullableProperties = $additionalProperties
}
else {
if ($nullableProperties.Count -eq 1) {
$nullableProperties = @($nullableProperties)
}
if ($additionalProperties -ne $null) {
$nullableProperties += $additionalProperties
}
}
if ($nullableProperties -ne $null -And $keyProperties -ne $null)
{
if ($keyProperties.Count -eq 1) {
$keyProperties = @($keyProperties)
}
$keys = $keyProperties | ForEach-Object {$_.Name}
if ($keys.Count -eq 1) {
$keys = @($keys)
}
$nullableProperties = $nullableProperties | Where-Object {$keys.Contains($_.Name) -eq $false}
}
$nullableProperties
}
#########################################################
# GetNavigationPropertyTypeName is a
# helper function used to fetch the type corresponding
# to navigation property in this metadata. This is
# network controller specific logic.
#########################################################
function GetNavigationPropertyTypeName
{
param
(
$navigationProperty,
$metaData
)
foreach($association in $metaData.Associations)
{
if ($association.Name -ne $navigationProperty.AssociationName -Or $association.Namespace -ne $navigationProperty.AssociationNamespace)
{
continue
}
# Now get the type for this association
if ($association.Type.NavPropertyName1 -eq $navigationProperty.Name)
{
$type = $association.Type.EndType1
$multiplicity = $association.Type.Multiplicity1
}
elseif ($associationType.NavPropertyName2 -eq $navigationProperty.Name)
{
$type = $association.Type.EndType2
$multiplicity = $association.Type.Multiplicity2
}
break
}
$fullName = $type.Namespace + '.' + $type.Name
# Check the multiplicity and convert to array if needed
if ($multiplicity -eq "*")
{
$fullName = "Collection($fullName)"
}
$fullName
}