Files
PowerShell/src/Modules/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataAdapter.ps1
T

2071 lines
167 KiB
PowerShell

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
}