src/metadata/GraphBuilder.ps1
# Copyright 2019, Adam Edwards # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. . (import-script GraphDataModel) . (import-script EntityEdge) . (import-script EntityVertex) enum BuildFlags { NavigationsProcessed = 1 MethodsProcessed = 2 CopiedToSingleton = 4 } ScriptClass GraphBuilder { $graphEndpoint = $null $version = $null $dataModel = $null $namespace = $null static { $AllBuildFlags = ([BuildFlags]::NavigationsProcessed) -bOR ([BuildFlags]::MethodsProcessed) -bOR ([BuildFlags]::CopiedToSingleton) } function __initialize($graphEndpoint, $version, $dataModel) { $this.graphEndpoint = $graphEndpoint $this.version = $version $this.dataModel = $dataModel $this.namespace = $this.dataModel |=> GetNamespace } function InitializeGraph($graph) { $metadataActivity = "Building graph version '$($this.version)' for endpoint '$($this.graphEndpoint)'" $::.ProgressWriter |=> WriteProgress -id 1 -activity $metadataActivity __AddRootVertices $graph } function __AddEntityTypeVertex($graph, $typeName) { AddEntityTypeVertices $graph $typeName } function __AddRootVertices($graph) { $singletons = $this.dataModel |=> GetSingletons __AddVerticesFromSchemas $graph $singletons $entitySets = $this.dataModel |=> GetEntitySets __AddVerticesFromSchemas $graph $entitySets } function __AddVerticesFromSchemas($graph, $schemas) { $schemas | foreach { __AddVertex $graph $_ } } function __AddVertex($graph, $schema) { $entity = new-so Entity $schema $this.namespace $graph |=> AddVertex $entity } function AddEntityTypeVertices($graph, $unqualifiedTypeName) { $qualifiedTypeName = $graph.namespace, $unqualifiedTypeName -join '.' $entityType = $this.dataModel |=> GetEntityTypeByName $qualifiedTypeName if ( $unqualifiedTypeName -and $entityType -eq $null ) { throw "Type '$unqualifiedTypeName' does not exist in the schema for the graph at endpoint '$($graph.endpoint)' with API version '$($graph.apiversion)'" } $::.ProgressWriter |=> WriteProgress -id 1 -activity "Adding type '$unqualifiedTypeName'" __AddVerticesFromSchemas $graph $entityType } function __AddEdgesToEntityTypeVertex($graph, $sourceVertex) { if ( $sourceVertex.TestFlags([BuildFlags]::NavigationsProcessed) ) { return } $transitions = if ( $sourceVertex.entity.navigations ) { $sourceVertex.entity.navigations } else { @() } $transitions | foreach { $transition = $_ $sink = $graph |=> TypeVertexFromTypeName $transition.typedata.entitytypename if ( ! $sink ) { $name = $transition.typedata.entitytypename $unqualifiedName = $name.substring($graph.namespace.length + 1, $name.length - $graph.namespace.length - 1) $sinkSchema = $this.dataModel |=> GetEntityTypeByName $name if ( $sinkSchema ) { __AddEntityTypeVertex $graph $unqualifiedName $sink = $graph |=> TypeVertexFromTypeName $transition.typedata.entitytypename } else { write-verbose "Unable to find schema for '$($transition.type)', $($transition.typedata.entitytypename)" } } if ( $sink ) { $edge = new-so EntityEdge $sourceVertex $sink $transition $sourceVertex |=> AddEdge $edge } else { write-verbose "Unable to find entity type for '$($transition.type)', $($transition.typedata.entitytypename), skipping" } } $sourceVertex.SetFlags([BuildFlags]::NavigationsProcessed) } function AddEdgesToVertex($graph, $vertex, $skipIfExist) { if ( $vertex.TestFlags($::.GraphBuilder.AllBuildFlags) ) { if ( !$skipIfExist ) { throw "Vertex '$($vertex.name)' already has edges" } return } $qualifiedTypeName = $vertex.entity.typedata.entitytypename $unqualifiedTypeName = $qualifiedTypeName.substring($graph.namespace.length + 1, $qualifiedTypename.length - $graph.namespace.length - 1) $::.ProgressWriter |=> WriteProgress -id 1 -activity "Adding edges for '$($vertex.name)'" __AddEdgesToEntityTypeVertex $graph $vertex if ( $vertex.entity.type -ne 'Singleton' ) { __AddMethodTransitionsToVertex $graph $vertex } else { __CopyEntityTypeEdgesToSingletonVertex $graph $vertex } } function __CopyEntityTypeEdgesToSingletonVertex($graph, $source) { if ( $source.TestFlags([BuildFlags]::CopiedToSingleton) ) { throw "Data from type already copied to singleton '$($source.name)'" } $entityName = ($source.entity.typeData).EntityTypeName $typeVertex = $graph |=> TypeVertexFromTypeName $entityName if ( $typeVertex -eq $null ) { throw "Unable to find an entity type for singleton '$($_.name)' and '$entityName'" } AddEdgesToVertex $graph $typeVertex $true $edges = $typeVertex.outgoingEdges.values | foreach { if ( ( $_ | gm transition ) -ne $null ) { $_ } } $edges | foreach { $sink = $_.sink $transition = $_.transition $edge = new-so EntityEdge $source $sink $transition $source |=> AddEdge $edge } $source.SetFlags([BuildFlags]::CopiedToSingleton) } function __AddMethodTransitionsToVertex($graph, $sourceVertex) { if ( $sourceVertex.TestFlags([BuildFlags]::MethodsProcessed) ) { write-verbose "Methods already processed for $($sourceVertex.name), skipping method addition" return } $sourceTypeName = $sourceVertex.entity.typeData.EntityTypeName $methods = $this.dataModel |=> GetMethodBindingsForType $sourceTypeName if ( ! $methods ) { write-verbose "Vertex ($sourceVertex.name) has no methods, skipping method addition" return } $methods | foreach { $method = $_ $sink = if ( $method | gm ReturnType ) { $typeName = if ( $method.localname -eq 'function' ) { $method.ReturnType | select -expandproperty Type } else { $method.ReturnType | select -expandproperty Type } $typeVertex = $graph |=> TypeVertexFromTypeName $typeName if ( $typeVertex -eq $null ) { $name = $typeName $unqualifiedName = if ( $name.startswith($graph.namespace) ) { $name.substring($graph.namespace.length + 1, $name.length - $graph.namespace.length - 1) } if ( $unqualifiedName ) { try { __AddEntityTypeVertex $graph $unqualifiedName $typeVertex = $graph |=> TypeVertexFromTypeName $typeName } catch { # Possibly an enumeration type, this will just be considered a scalar } } else { write-verbose "Unable to find schema for method '$($method.name)' with type '$typeName'" } } if ( $typeVertex ) { $typeVertex } else { write-verbose "Type $($typeName) returned by $($method.name) cannot be found, configuring Scalar vertex" $::.EntityVertex.ScalarVertex } } else { $::.Entityvertex.NullVertex } __AddMethod $sourceVertex $method $sink } $sourceVertex.SetFlags([BuildFlags]::MethodsProcessed) } function __AddMethod($targetVertex, $methodSchema, $returnTypeVertex) { if ( ! ($targetVertex |=> EdgeExists($methodSchema.name)) ) { $methodEntity = new-so Entity $methodSchema $this.namespace $edge = new-so EntityEdge $targetVertex $returnTypeVertex $methodEntity $targetVertex |=> AddEdge $edge } else { write-verbose "Skipped add of edge $($methodSchema.name) to $($returnTypeVertex.id) from vertex $($targetVertex.id) because it already exists." } } } |