├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── PRIVACY.md ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── ROADMAP.md ├── TERMS.md ├── assets └── PoshGraphIcon.png ├── autographps.psd1 ├── autographps.psm1 ├── autographps.tests.ps1 ├── azure-pipelines.yml ├── build ├── Get-CommandParameters.ps1 ├── Get-DocumentStationStatus.ps1 ├── Get-MissingDocCommandsByFewestParameters.ps1 ├── Init-DirectTestRun.ps1 ├── README.md ├── bubble.ps1 ├── build-package.ps1 ├── clean-build.ps1 ├── common-build-functions.ps1 ├── configure-tools.ps1 ├── import-devmodule.ps1 ├── install-devmodule.ps1 ├── install-fromsource.ps1 ├── install.ps1 ├── publish-modulepackage.ps1 ├── publish-moduletodev.ps1 ├── quickstart.ps1 └── welcome.ps1 ├── docs ├── MOTIVATION.md └── WALKTHROUGH.md ├── packages.config ├── src ├── TestAssets │ └── GraphPing200.json ├── aliases.ps1 ├── client │ ├── GraphLocalSettings.ps1 │ └── LocationContext.ps1 ├── cmdlets.ps1 ├── cmdlets │ ├── Add-GraphRelatedItem.ps1 │ ├── Find-GraphPermission.ps1 │ ├── Find-GraphType.ps1 │ ├── Get-Graph.ps1 │ ├── Get-Graph.tests.ps1 │ ├── Get-GraphChildItem.ps1 │ ├── Get-GraphItem.ps1 │ ├── Get-GraphItem.tests.ps1 │ ├── Get-GraphItemRelationship.ps1 │ ├── Get-GraphItemUri.ps1 │ ├── Get-GraphLastOutput.ps1 │ ├── Get-GraphLocation.ps1 │ ├── Get-GraphMember.ps1 │ ├── Get-GraphMethod.ps1 │ ├── Get-GraphRelatedItem.ps1 │ ├── Get-GraphResourceWithMetadata.ps1 │ ├── Get-GraphResourceWithMetadata.tests.ps1 │ ├── Get-GraphType.ps1 │ ├── Get-GraphType.tests.ps1 │ ├── Get-GraphUri.ps1 │ ├── Get-GraphUri.tests.ps1 │ ├── Get-GraphUriInfo.ps1 │ ├── Invoke-GraphMethod.ps1 │ ├── Measure-Graph.ps1 │ ├── New-Graph.ps1 │ ├── New-GraphItem.ps1 │ ├── New-GraphItem.tests.ps1 │ ├── New-GraphItemRelationship.ps1 │ ├── New-GraphMethodParameterObject.ps1 │ ├── New-GraphObject.ps1 │ ├── New-GraphObject.tests.ps1 │ ├── Remove-Graph.ps1 │ ├── Remove-GraphItem.ps1 │ ├── Remove-GraphItemRelationship.ps1 │ ├── Set-GraphItem.ps1 │ ├── Set-GraphItem.tests.ps1 │ ├── Set-GraphLocation.ps1 │ ├── Set-GraphPrompt.ps1 │ ├── Show-GraphHelp.ps1 │ ├── Update-GraphMetadata.ps1 │ ├── common │ │ ├── AutoGraphFormats.ps1xml │ │ ├── ContextHelper.ps1 │ │ ├── FunctionParameterHelper.ps1 │ │ ├── FunctionSegmentBuilder.ps1 │ │ ├── GraphParameterCompleter.ps1 │ │ ├── GraphStatisticsDisplayType.ps1 │ │ ├── GraphUriParameterCompleter.ps1 │ │ ├── LocationHelper.ps1 │ │ ├── MemberDisplayType.ps1 │ │ ├── MemberParameterCompleter.ps1 │ │ ├── MetaGraphFormatter.ps1 │ │ ├── MethodDisplayType.ps1 │ │ ├── MethodNameParameterCompleter.ps1 │ │ ├── MethodParameterParameterCompleter.ps1 │ │ ├── MethodUriParameterCompleter.ps1 │ │ ├── PermissionHelper.ps1 │ │ ├── QueryTranslationHelper.ps1 │ │ ├── RelationshipDisplayType.ps1 │ │ ├── RequestHelper.ps1 │ │ ├── SegmentHelper.ps1 │ │ ├── TypeHelper.ps1 │ │ ├── TypeMemberFinder.ps1 │ │ ├── TypeParameterCompleter.ps1 │ │ ├── TypePropertyParameterCompleter.ps1 │ │ ├── TypeSearchResultDisplayType.ps1 │ │ ├── TypeUriHelper.ps1 │ │ └── TypeUriParameterCompleter.ps1 │ └── new-graph.tests.ps1 ├── common │ ├── CustomFormats.ps1xml │ ├── CustomFormatter.ps1 │ ├── GraphAccessDeniedException.ps1 │ └── PreferenceHelper.ps1 ├── formats.ps1 ├── graph.ps1 ├── metadata │ ├── Entity.ps1 │ ├── EntityEdge.ps1 │ ├── EntityGraph.ps1 │ ├── EntityVertex.ps1 │ ├── GraphBuilder.ps1 │ ├── GraphCache.ps1 │ ├── GraphDataModel.ps1 │ ├── GraphManager.ps1 │ ├── GraphSegment.ps1 │ ├── QualifiedSchema.ps1 │ ├── SegmentParser.ps1 │ ├── UriCache.ps1 │ └── metadata.ps1 ├── settings.ps1 └── typesystem │ ├── CompositeTypeProvider.ps1 │ ├── GraphObjectBuilder.ps1 │ ├── MethodInfo.ps1 │ ├── ScalarTypeProvider.ps1 │ ├── TYPES_DESIGN.md │ ├── TypeDefinition.ps1 │ ├── TypeIndex.ps1 │ ├── TypeIndexEntry.ps1 │ ├── TypeManager.ps1 │ ├── TypeMatch.ps1 │ ├── TypeMember.ps1 │ ├── TypeProvider.ps1 │ ├── TypeSchema.ps1 │ ├── TypeSearcher.ps1 │ ├── TypeSearcher.tests.ps1 │ ├── TypeTable.ps1 │ └── typesystem.ps1 └── test ├── CI ├── Get-CIPipelineCredential.ps1 ├── PesterDirectRunInit.ps1 └── RunIntegrationTests.ps1 ├── Clean-IntegrationTestState.ps1 ├── Get-IntegrationTestAppCertificates.ps1 ├── Get-IntegrationTestEntityName.ps1 ├── Get-IntegrationTestStatus.ps1 ├── Initialize-IntegrationTestEnvironment.ps1 ├── IsIntegrationTestRun.ps1 ├── Set-IntegrationTestStatus.ps1 ├── assets ├── GetGraphUriMe.json ├── NewGraphBetaObjectApplication.json ├── NewGraphBetaObjectCallRecord.json ├── NewGraphObjectBetaCallRecord.json ├── betametadata-ns-alias-2020-01-23.xml ├── betametadata-ns-alias-multi-namespace-2020-03-25.xml ├── metadata.xml ├── microsoft-directoryservices-fragment.xml ├── v1metadata-no-ns-alias-2020-01-20.xml └── v1metadata-ns-alias-2020-01-22.xml ├── common ├── CompareCustomObject.ps1 ├── GetParameterTestFunction.ps1 └── NoninteractiveSubtestHelper.ps1 └── integration ├── cmdlets ├── Get-GraphRelatedItem.tests.ps1 ├── Get-GraphResourceWithMetadata.tests.ps1 └── Test-Graph.tests.ps1 └── pipeline └── GraphResponseObject.tests.ps1 /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Run some sequence of commands 13 | 2. View the output 14 | 15 | **Expected behavior** 16 | A clear and concise description of what you expected to happen. 17 | 18 | **Screenshots** 19 | If applicable, add screenshots to help explain your problem. 20 | 21 | **Desktop (please complete the following information):** 22 | - OS: [e.g. Windows 10 RS 4] 23 | - AutoGraphPS module Version [e.g. 0.14.3] 24 | 25 | **Logs -- please attach the following data for failures executing a command 26 | - Output of the AutoGraphPS command using the `-verbose` option 27 | 28 | **Additional context** 29 | Add any other context about the problem here. 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is and how the feature's absence negatively impacts you or blocks a common use case. 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | pkg/* 2 | lib/* 3 | bin/* 4 | obj/* 5 | tmplib/* 6 | test/results/* 7 | .psrepo/* 8 | .devmodule/* 9 | .doc/* 10 | .test/* 11 | .testconfig/* 12 | .logs/* 13 | NuGet.Config 14 | localnuget.config 15 | poshgraph.psd1.tmp.psd1 16 | *~ 17 | .chef 18 | .#* 19 | *# 20 | *.dll 21 | .bubble/* 22 | BubbleFile 23 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at adamedx@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /PRIVACY.md: -------------------------------------------------------------------------------- 1 | # Privacy Policy 2 | 3 | ## In general 4 | 5 | **[AutoGraphPS](https://github.com/adamedx/autographps)** is a client-side tool for interacting with the [Microsoft Graph API](https://graph.microsoft.io). **AutoGraphPS** does not maintain any store of data involving your usage of the tool. 6 | 7 | ## Data collection 8 | 9 | In the course of usage of the tool, **AutoGraphPS** sends data you specify, including personal identifiers, to Microsoft's [Azure Active Directory](https://azure.microsoft.com/en-us/services/active-directory/) and [Microsoft Graph API](https://graph.microsoft.io) services. **AutoGraphPS** does not send your personal data to any other services. 10 | 11 | ## Microsoft Graph and data flows 12 | 13 | **AutoGraphPS** is designed to facilitate human and automated interaction with the Microsoft Graph. Thus as part of your use of **AutoGraphPS**, data are sent to and received from the Microsoft Graph. While **AutoGraphPS** maintains no record of the data sent or received, the Microsoft Graph API service may record data related to the communication initiated by your use of **AutoGraphPS.** For more information on privacy policies related to the Microsoft Graph API, see [Microsoft's Privacy Policy](https://privacy.microsoft.com/en-us/privacystatement) and related relevant documentation from Microsoft. 14 | 15 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Description 2 | 3 | [Describe the end user impact of this change, i.e. problem behavior resolved or new functionality added] 4 | 5 | ### Dependency changes 6 | 7 | [Describe any dependencies added or removed, including changes to the versions of existing dependencies] 8 | 9 | ### Implementation notes 10 | 11 | [Give a technical explanation of the change. For code defects, please describe the root cause and the change made in terms of that cause. For features, describe the new classes, cmdlets, or methods added and how they relate to each other.] 12 | 13 | ### Linked issues 14 | 15 | [List any existing issues this PR resolves, and also link any previous pull requests or commits that introduced the code defect resolved by this change or are otherwise related] 16 | 17 | ### Checklist 18 | 19 | - [ ] New test coverage was added for the new functionality 20 | - [ ] All project tests pass successfully 21 | - [ ] All commits have a sign-off for the Developer Certificate of Origin. See 22 | -------------------------------------------------------------------------------- /TERMS.md: -------------------------------------------------------------------------------- 1 | # Terms of Use 2 | 3 | ## Terms of Use of this Software 4 | 5 | The Terms of Use for **AutoGraphPS** are described in the [LICENSE](LICENSE.md). 6 | 7 | ## Terms of Use of Microsoft Graph 8 | 9 | This software, **AutoGraphPS** communicates with the [Microsoft Graph API](https://graph.microsoft.io). When you use **AutoGraphPS** or any other software to interact with the Microsoft Graph, you are also subject to the [Microsoft Graph Terms of Use](https://developer.microsoft.com/en-us/graph/docs/misc/terms-of-use). 10 | -------------------------------------------------------------------------------- /assets/PoshGraphIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamedx/autographps/5e154c1fb03d3074d59f429639936f50a1b76dd3/assets/PoshGraphIcon.png -------------------------------------------------------------------------------- /autographps.psm1: -------------------------------------------------------------------------------- 1 | # Copyright 2023, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (join-path $psscriptroot src/graph.ps1) 16 | 17 | $functions = @( 18 | 'Add-GraphRelatedItem', 19 | 'Find-GraphType', 20 | 'Find-GraphPermission', 21 | 'Get-Graph', 22 | 'Get-GraphItem', 23 | 'Get-GraphItemRelationship', 24 | 'Get-GraphRelatedItem', 25 | 'Get-GraphItemUri', 26 | 'Get-GraphChildItem', 27 | 'Get-GraphResourceWithMetadata', 28 | 'Get-GraphLastOutput', 29 | 'Get-GraphLocation', 30 | 'Get-GraphMember', 31 | 'Get-GraphMethod', 32 | 'Get-GraphType', 33 | 'Get-GraphUri', 34 | 'Get-GraphUriInfo', 35 | 'Invoke-GraphMethod', 36 | 'Measure-Graph', 37 | 'New-Graph', 38 | 'New-GraphItem', 39 | 'New-GraphItemRelationship', 40 | 'New-GraphMethodParameterObject', 41 | 'New-GraphObject', 42 | 'Remove-Graph', 43 | 'Remove-GraphItem', 44 | 'Remove-GraphItemRelationship', 45 | 'Set-GraphItem', 46 | 'Set-GraphLocation', 47 | 'Set-GraphPrompt', 48 | 'Show-GraphHelp', 49 | 'Update-GraphMetadata' 50 | ) 51 | 52 | $aliases = @('gcd', 'gg', 'glo', 'ggrel', 'gri', 'ggu', 'guri', 'ggci', 'ggi', 'gls', 'ggm', 'ggmt', 'gwd', 'gni', 'grm', 'gsi', 'igm', 'ngo', 'ngp') 53 | 54 | # Note that in order to make variables from nested modules 55 | # accessible without the user directly invoking them, 56 | # we need to "forward" them by exporting them even 57 | # though they come from a module other than this one. 58 | $variables = @( 59 | 'GraphAutoPromptPreference' 60 | 'GraphMetadataPreference' 61 | 'GraphPromptColorPreference' 62 | 'GraphVerboseOutputPreference' # From AutoGraphPS-SDK 63 | 'LastGraphItems' # From AutoGraphPS-SDK 64 | 'AutoGraphColorModePreference' # From AutoGraphPS-SDK 65 | ) 66 | 67 | export-modulemember -function $functions -alias $aliases -variable $variables 68 | -------------------------------------------------------------------------------- /autographps.tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | Describe "Autographps application" { 16 | $manifestLocation = Join-Path $psscriptroot 'autographps.psd1' 17 | 18 | function Get-ModuleMetadataFromManifest ( $moduleName, $manifestPath ) { 19 | # Load the module contents and deserialize it by evaluating 20 | # it (module files are just hash tables expressed as PowerShell script) 21 | $moduleContentLines = get-content $manifestPath 22 | $moduleData = $moduleContentLines | out-string | iex 23 | $moduleData['Name'] = $moduleName 24 | $moduledata 25 | } 26 | 27 | $manifest = Get-ModuleMetadataFromManifest 'autographps' $manifestlocation 28 | 29 | Context "When loading the manifest" { 30 | It "should export the exact same set of functions as are in the set of expected functions" { 31 | $expectedFunctions = @( 32 | 'Add-GraphRelatedItem' 33 | 'Find-GraphPermission' 34 | 'Find-GraphType' 35 | 'Get-Graph' 36 | 'Get-GraphChildItem' 37 | 'Get-GraphItem' 38 | 'Get-GraphItemRelationship' 39 | 'Get-GraphRelatedItem' 40 | 'Get-GraphItemUri' 41 | 'Get-GraphResourceWithMetadata' 42 | 'Get-GraphLastOutput' 43 | 'Get-GraphLocation' 44 | 'Get-GraphMember' 45 | 'Get-GraphMethod' 46 | 'Get-GraphType' 47 | 'Get-GraphUri' 48 | 'Get-GraphUriInfo' 49 | 'Invoke-GraphMethod' 50 | 'Measure-Graph' 51 | 'New-Graph' 52 | 'New-GraphItem' 53 | 'New-GraphItemRelationship' 54 | 'New-GraphMethodParameterObject' 55 | 'New-GraphObject' 56 | 'Remove-Graph' 57 | 'Remove-GraphItem' 58 | 'Remove-GraphItemRelationship' 59 | 'Set-GraphItem', 60 | 'Set-GraphLocation' 61 | 'Set-GraphPrompt' 62 | 'Show-GraphHelp' 63 | 'Update-GraphMetadata') 64 | 65 | $manifest.FunctionsToExport.count | Should BeExactly $expectedFunctions.length 66 | 67 | $verifiedExportsCount = 0 68 | 69 | $expectedFunctions | foreach { 70 | if ( $manifest.FunctionsToExport -contains $_ ) { 71 | $verifiedExportsCount++ 72 | } 73 | { get-command $_ | out-null } | Should Not Throw 74 | } 75 | 76 | $verifiedExportsCount | Should BeExactly $expectedFunctions.length 77 | } 78 | 79 | It "should export the exact same set of aliases as are in the set of expected aliases" { 80 | $expectedAliases = @('gcd', 'glo', 'gg', 'ggrel', 'gri', 'ggu', 'guri', 'ggci', 'ggi', 'gls', 'ggm', 'ggmt', 'gwd', 'gni', 'grm', 'gsi', 'igm', 'ngo', 'ngp') 81 | 82 | $manifest.AliasesToExport.count | Should BeExactly $expectedAliases.length 83 | 84 | $verifiedExportsCount = 0 85 | 86 | $expectedAliases | foreach { 87 | if ( $manifest.AliasesToExport -contains $_ ) { 88 | $verifiedExportsCount++ 89 | } 90 | $_ | get-alias | select -expandproperty resolvedcommandname | get-command | select -expandproperty module | select -expandproperty name | Should Be $manifest.Name 91 | } 92 | 93 | $verifiedExportsCount | Should BeExactly $expectedAliases.length 94 | } 95 | } 96 | 97 | Context "When invoking the Autographps application" { 98 | It "Should be able to create a connection object" { 99 | $connection = New-GraphConnection 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /build/Get-CommandParameters.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [cmdletbinding()] 16 | param( 17 | [parameter(valuefrompipeline=$true)] 18 | [string] $commandName 19 | ) 20 | begin { 21 | $targetModuleName = (join-path $psscriptroot .. | gi).name 22 | $module = Get-Module $targetModuleName -listavailable 23 | 24 | if ( ! $module ) { 25 | throw "This command must be executed from a PowerShell session that has successfully imported the module '$targetModuleName'" 26 | } 27 | } 28 | 29 | process { 30 | Set-StrictMode -Version 2 31 | 32 | $commandList = if ( ! $commandName ) { 33 | $module.exportedfunctions.keys 34 | } else { 35 | @($commandName) 36 | } 37 | 38 | foreach ( $command in $commandList ) { 39 | $commandData = Get-Command $command 40 | $parameters = $commandData.parameters.keys 41 | [PSCustomobject] @{ 42 | Command = $command 43 | ParameterCount = ( $parameters | measure-object ).count 44 | Parameters = $parameters 45 | } 46 | } 47 | } 48 | 49 | end { 50 | } 51 | -------------------------------------------------------------------------------- /build/Get-DocumentStationStatus.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [cmdletbinding()] 16 | param() 17 | Set-StrictMode -Version 2 18 | $commandRoot = join-path $psscriptroot './src/cmdlets' 19 | 20 | $commandFiles = ls ./src/cmdlets -filter *.ps1 | 21 | where name -notlike '*.tests.*' | 22 | where name -notlike '*~*' | 23 | where name -notlike '*#*' 24 | 25 | $missingDocCommandFiles = $commandFiles | 26 | where { ! ( $_ | select-string -CaseSensitive '\.SYNOPSIS' ) } 27 | sort-object Length 28 | 29 | $commandCount = ( $commandFiles | measure-object ).count 30 | $missingDocCommandCount = ($missingDocCommandFiles |measure-object).count 31 | 32 | $completionPercent = if ( $commandCount ) { 33 | ( 1 - $missingDocCommandCount / $commandCount ) * 100 34 | } 35 | 36 | $statusTime = [DateTime]::Now 37 | $commandNames = $missingDocCommandFiles | select-object -expandproperty Name | 38 | foreach { 39 | ($_ -split '\.')[0] 40 | } 41 | 42 | [PSCustomObject] @{ 43 | ReportTime = $statusTime 44 | DocCompletionPercent = $completionPercent 45 | MissingDocCommandCount = $missingDocCommandCount 46 | TotalCommandCount = $commandCount 47 | CommandsMissingDocs = $commandNames 48 | } 49 | -------------------------------------------------------------------------------- /build/Get-MissingDocCommandsByFewestParameters.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [cmdletbinding()] 16 | param() 17 | Set-StrictMode -Version 2 18 | 19 | & $psscriptroot/Get-DocumentStationStatus | 20 | select-object -expandproperty commandsMissingDocs | 21 | & $psscriptroot/Get-CommandParameters | sort-object parametercount 22 | 23 | -------------------------------------------------------------------------------- /build/Init-DirectTestRun.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2019, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [cmdletbinding()] 16 | param() 17 | 18 | . "$psscriptroot/common-build-functions.ps1" 19 | 20 | $testDir = join-path (Get-SourceRootDirectory) test/CI 21 | $testInitPath = join-path $testDir PesterDirectRunInit.ps1 22 | 23 | $devDirectory = Get-DevModuleDirectory 24 | $newpsmodulepath = $devDirectory + $OSPathSeparator + (gi env:PSModulePath).value 25 | si env:PSModulePath $newpsmodulepath 26 | write-verbose "Updated PSModulePath environment variable to '$newpsmodulepath'" 27 | 28 | if ( test-path $testInitPath ) { 29 | write-verbose "Found init script '$testInitPath', will execute it" 30 | . $testInitPath 31 | } else { 32 | write-verbose "No init script found at '$testInitPath', skipping direct test run init" 33 | } 34 | 35 | -------------------------------------------------------------------------------- /build/build-package.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2019, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [cmdletbinding()] 16 | param($targetDirectory = $null, [switch] $noclean, [switch] $DownloadDependencies) 17 | 18 | . "$psscriptroot/common-build-functions.ps1" 19 | 20 | if ( $DownloadDependencies.ispresent ) { 21 | $installScriptPath = join-path (get-sourcerootdirectory) 'build\install.ps1' 22 | & $installScriptPath | out-null 23 | } 24 | 25 | $moduleManifestPath = Get-ModuleManifestPath 26 | $moduleOutputDirectory = new-moduleoutputdirectory $targetDirectory (! $noclean.ispresent) 27 | $moduleOutputRootDirectory = Get-ModuleOutputRootDirectory 28 | 29 | Generate-ReferenceModules $moduleManifestPath $moduleOutputRootDirectory 30 | 31 | $module = Get-ModuleFromManifest $moduleManifestPath $moduleOutputRootDirectory 32 | 33 | $inputs = @( 34 | $module, 35 | $moduleOutputDirectory 36 | ) 37 | 38 | $nocleanArgument = @{noclean=$noclean} 39 | $moduleOutputPath = build-module $inputs[0] $inputs[1] @nocleanArgument -includeInstalledlibraries 40 | 41 | write-host "Module placed at '$moduleOutputPath'." 42 | 43 | write-host -foregroundcolor green "Build succeeded." 44 | 45 | -------------------------------------------------------------------------------- /build/clean-build.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2019, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [cmdletbinding()] 16 | param([switch] $IncludeTools, [switch] $All, [switch] $IntegrationTestOnly ) 17 | 18 | . "$psscriptroot/common-build-functions.ps1" 19 | 20 | if ( $IntegrationTestOnly.IsPresent ) { 21 | Clean-TestDirectories 22 | } else { 23 | if ( $IncludeTools.IsPresent -or $All.IsPresent ) { 24 | clean-tools 25 | } 26 | 27 | clear-TemporaryPSModuleSources 28 | 29 | clean-builddirectories 30 | } 31 | 32 | 33 | -------------------------------------------------------------------------------- /build/install-devmodule.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2019, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [cmdletbinding()] 16 | param($scope = 'CurrentUser') 17 | 18 | . "$psscriptroot/common-build-functions.ps1" 19 | 20 | $moduleName = Get-ModuleName 21 | $repository = get-temporarymodulepsrepository $moduleName (Get-DevRepoDirectory) 22 | 23 | try { 24 | install-module $moduleName -repository $repository -scope $scope -verbose -force 25 | } finally { 26 | unregister-psrepository $repository 27 | } 28 | 29 | write-host "Successfully installed module '$moduleName' with scope '$scope'." 30 | write-host -foregroundcolor yellow "You may need to restart PowerShell for changes to take effect." 31 | write-host -foregroundcolor green "Installation succeeded." 32 | 33 | -------------------------------------------------------------------------------- /build/install-fromsource.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2019, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [cmdletbinding(positionalbinding=$false)] 16 | param($DependencyRepository = $null, $Scope = 'CurrentUser') 17 | 18 | & "$psscriptroot/clean-build.ps1" 19 | 20 | & "$psscriptroot/build-package.ps1" -Downloaddependencies 21 | 22 | & "$psscriptroot/publish-moduletodev.ps1" $DependencyRepository 23 | 24 | & "$psscriptroot/install-devmodule.ps1" $Scope 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /build/publish-modulepackage.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2019, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [cmdletbinding()] 16 | param($targetRepository = 'psgallery', $repositoryKeyFile = $null, [switch] $noclean, [switch] $force) 17 | 18 | . "$psscriptroot/common-build-functions.ps1" 19 | 20 | $moduleManifestPath = Get-ModuleManifestPath 21 | $moduleOutputRootDirectory = Get-ModuleOutputRootDirectory 22 | 23 | Generate-ReferenceModules $moduleManifestPath $moduleOutputRootDirectory 24 | 25 | $module = Get-ModuleFromManifest $moduleManifestPath $moduleOutputRootDirectory 26 | 27 | $moduleOutputPath = join-path (Get-OutputDirectory) "$moduleOutputSubdirectory/$($module.name)/$($module.version)" 28 | 29 | write-host "Publishing module at '$moduleOutputPath' to PS module repository '$targetRepository'..." 30 | 31 | $repositoryKey = if ( $repositoryKeyFile -ne $null ) { 32 | Get-RepositoryKeyFromFile $repositoryKeyFile 33 | } 34 | 35 | $forceArgument = @{force=$force} 36 | 37 | publish-modulebuild $moduleOutputPath $targetRepository $repositoryKey @forceArgument | out-null 38 | 39 | write-host "Module '$($module.name)' successfully published to repository $targetRepository." 40 | write-host -foregroundcolor green "Publish module succeeded." 41 | -------------------------------------------------------------------------------- /build/publish-moduletodev.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2019, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [cmdletbinding()] 16 | param($sourceDependencyRepository = $null, $targetDirectory) 17 | 18 | . "$psscriptroot/common-build-functions.ps1" 19 | 20 | $moduleName = Get-ModuleName 21 | 22 | $locations = publish-modulelocal $sourceDependencyRepository $targetDirectory -verbose 23 | 24 | $moduleLocation = $locations.ImportableModuleDirectory 25 | $repoLocation = $locations.ModulePackageRepositoryDirectory 26 | 27 | write-host "Module '$moduleName' and its dependencies published to importable module location '$moduleLocation'." 28 | write-host "A nuget package for the module and packages for dependencies was published to nuget compatible directory '$repoLocation'" 29 | write-host -foregroundcolor green "Publish to local developer location succeeded." 30 | -------------------------------------------------------------------------------- /build/quickstart.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2019, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | param($sourceDependencyRepository = $null) 16 | 17 | & "$psscriptroot/clean-build.ps1" 18 | 19 | & "$psscriptroot/build-package.ps1" -Downloaddependencies 20 | 21 | & "$psscriptroot/publish-moduletodev.ps1" $sourceDependencyRepository 22 | 23 | & "$psscriptroot/import-devmodule.ps1" "$psscriptroot/welcome.ps1" 24 | 25 | write-host -foregroundcolor green "Quickstart succeeded." 26 | 27 | -------------------------------------------------------------------------------- /build/welcome.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2019, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | write-host -foregroundcolor cyan "`nWelcome to AutoGraphPS!`n" 16 | write-host "To get started, try executing any of the following commands:" 17 | @( 18 | [PSCustomObject]@{Command=" Test-Graph";Purpose="# Retrieves diagnostic information from a Microsoft Graph Service endpoint"} 19 | [PSCustomObject]@{Command=" Connect-Graph";Purpose="# Establishes a convenient connection context; no need to re-auth for each command"} 20 | [PSCustomObject]@{Command=" Get-GraphToken";Purpose="# Gets information about Graph API versions such as v1.0, beta, etc."} 21 | [PSCustomObject]@{Command=" gls";Purpose="# Enumerates child uri graph segments of the current location."} 22 | [PSCustomObject]@{Command=" gcd me";Purpose="# Changes the current location to the 'me' segment of the graph."} 23 | [PSCustomObject]@{Command=" gwd";Purpose="# Output the current location."} 24 | [PSCustomObject]@{Command=" gls";Purpose="# Enumerates the children of the (new) current location."} 25 | [PSCustomObject]@{Command=" gls messages";Purpose="# Enumerates email messages ('me/messages')."} 26 | [PSCustomObject]@{Command=" Get-GraphItem /organization";Purpose="# For Entra ID accounts, gets organization information using an absolute path"} 27 | [PSCustomObject]@{Command=" Get-Graph";Purpose="# Outputs information about all the current graphs and their API versions"} 28 | [PSCustomObject]@{Command=" gcd /beta:";Purpose="# Mount an entirely new graph of API version 'beta' and change the current location to it."} 29 | [PSCustomObject]@{Command=" gls me";Purpose="# Enumerate the 'me' segment itself."} 30 | [PSCustomObject]@{Command=" Get-Graph";Purpose="# Output the currently mounted graphs."} 31 | ) | format-table -wrap -hidetableheaders | out-host 32 | write-host '' 33 | -------------------------------------------------------------------------------- /packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/aliases.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Aliases that are similar to preexisting, well-known aliases -- 16 | # Prefix these with 'g' to emphasize their implied similarity 17 | set-alias gcd Set-GraphLocation 18 | set-alias ggci Get-GraphChildItem 19 | set-alias ggi Get-GraphItem 20 | set-alias ggm Get-GraphMember 21 | set-alias gls Get-GraphResourceWithMetadata 22 | set-alias gwd Get-GraphLocation 23 | set-alias gni New-GraphItem 24 | set-alias grm Remove-GraphItem 25 | set-alias gsi Set-GraphItem 26 | 27 | # These aliases don't correspond to any familiar aliases, so 28 | # just name them by abbreviation 29 | set-alias ggu Get-GraphUriInfo 30 | set-alias guri Get-GraphUri 31 | set-alias glo Get-GraphLastOutput 32 | set-alias gg Get-Graph 33 | set-alias ggrel Get-GraphItemRelationship 34 | set-alias gri Get-GraphRelatedItem 35 | set-alias ggmt Get-GraphMethod 36 | set-alias igm Invoke-GraphMethod 37 | set-alias ngo New-GraphObject 38 | set-alias ngp New-GraphMethodParameterObject 39 | -------------------------------------------------------------------------------- /src/client/GraphLocalSettings.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | function __SettingVariableHelper([string] $variableName, $value) { 16 | if ( ! ( Get-Variable -scope:script $variableName ) ) { 17 | throw "Preference variable '$variableName' could not be found" 18 | } 19 | 20 | set-variable -scope:script $variableName $value 21 | } 22 | 23 | ScriptClass GraphLocalSettings { 24 | static { 25 | 26 | $setPreferenceScript = $null 27 | 28 | function __initialize([ScriptBlock] $setPreferenceScript) { 29 | $this.setPreferenceScript = $setPreferenceScript 30 | $::.LocalSettings |=> RegisterSettingProperties $::.LocalProfileSpec.ProfilesCollection $this.propertyReaders 31 | } 32 | 33 | $propertyReaders = @{ 34 | PromptBehavior = @{ 35 | Validator = 'StringValidator' 36 | Required = $false 37 | Updater = { 38 | $currentProfile = $::.LocalProfile |=> GetCurrentProfile 39 | if ( $currentProfile ) { 40 | $settingValue = $currentProfile.GetSetting('PromptBehavior') 41 | if ( $settingValue -in 'Auto', 'Enable', 'Disable' ) { 42 | . $::.GraphLocalSettings.setPreferenceScript __GraphPromptBehaviorSetting $settingValue 43 | } 44 | } 45 | } 46 | } 47 | 48 | PromptColor = @{ 49 | Validator = 'StringValidator' 50 | Required = $false 51 | Updater = { 52 | $currentProfile = $::.LocalProfile |=> GetCurrentProfile 53 | if ( $currentProfile ) { 54 | $settingValue = $currentProfile.GetSetting('PromptColor') 55 | if ( $settingValue -in [System.ConsoleColor].GetEnumNames() ) { 56 | . $::.GraphLocalSettings.setPreferenceScript __GraphPromptColorSetting $settingValue 57 | } 58 | } 59 | } 60 | } 61 | } 62 | } 63 | } 64 | 65 | $::.GraphLocalSettings |=> __initialize ( get-command __SettingVariableHelper ).ScriptBlock 66 | -------------------------------------------------------------------------------- /src/client/LocationContext.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2019, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script ../metadata/GraphSegment) 16 | 17 | ScriptClass LocationContext { 18 | static { 19 | function __initialize { 20 | $::.GraphContext |=> SetDefaultLocation $::.GraphSegment.RootSegment 21 | $::.GraphContext |=> GetCurrent |=> SetLocation $::.GraphSegment.RootSegment 22 | } 23 | } 24 | } 25 | 26 | $::.LocationContext |=> __initialize 27 | -------------------------------------------------------------------------------- /src/cmdlets.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script client/LocationContext) 16 | . (import-script cmdlets\New-GraphItemRelationship) 17 | . (import-script cmdlets\Find-GraphType) 18 | . (import-script cmdlets\Find-GraphPermission) 19 | . (import-script cmdlets\Get-Graph) 20 | . (import-script cmdlets\Get-GraphItem) 21 | . (import-script cmdlets\Get-GraphItemRelationship) 22 | . (import-script cmdlets\Get-GraphRelatedItem) 23 | . (import-script cmdlets\Get-GraphItemUri) 24 | . (import-script cmdlets\Get-GraphResourceWithMetadata) 25 | . (import-script cmdlets\Get-GraphChildItem) 26 | . (import-script cmdlets\Get-GraphLastOutput) 27 | . (import-script cmdlets\Get-GraphLocation) 28 | . (import-script cmdlets\Get-GraphMember) 29 | . (import-script cmdlets\Get-GraphMethod) 30 | . (import-script cmdlets\Get-GraphType) 31 | . (import-script cmdlets\Get-GraphUri) 32 | . (import-script cmdlets\Get-GraphUriInfo) 33 | . (import-script cmdlets\Invoke-GraphMethod) 34 | . (import-script cmdlets\Measure-Graph) 35 | . (import-script cmdlets\New-Graph) 36 | . (import-script cmdlets\New-GraphMethodParameterObject) 37 | . (import-script cmdlets\Remove-Graph) 38 | . (import-script cmdlets\Remove-GraphItem) 39 | . (import-script cmdlets\Remove-GraphItemRelationship) 40 | . (import-script cmdlets\Set-GraphItem) 41 | . (import-script cmdlets\Set-GraphLocation) 42 | . (import-script cmdlets\Set-GraphPrompt) 43 | . (import-script cmdlets\Show-GraphHelp) 44 | . (import-script cmdlets\Update-GraphMetadata) 45 | . (import-script cmdlets\New-GraphObject) 46 | . (import-script cmdlets\New-GraphItem) 47 | . (import-script cmdlets\Add-GraphRelatedItem) 48 | 49 | # Add parameter completion to commands exported by a different module as a UX enhancement 50 | $::.ParameterCompleter |=> RegisterParameterCompleter Invoke-GraphApiRequest Uri (new-so GraphUriParameterCompleter ([GraphUriCompletionType]::LocationOrMethodUri )) 51 | $::.ParameterCompleter |=> RegisterParameterCompleter Get-GraphResource Uri (new-so GraphUriParameterCompleter ([GraphUriCompletionType]::LocationOrMethodUri )) 52 | $::.ParameterCompleter |=> RegisterParameterCompleter Get-GraphResource Select (new-so TypeUriParameterCompleter Property) 53 | 54 | $::.ParameterCompleter |=> RegisterParameterCompleter Get-GraphResource Expand (new-so TypeUriParameterCompleter Property $false NavigationProperty) 55 | $::.ParameterCompleter |=> RegisterParameterCompleter Get-GraphResource OrderBy (new-so TypeUriParameterCompleter Property) 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/cmdlets/Get-Graph.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script common/ContextHelper) 16 | . (import-script common/GraphParameterCompleter) 17 | 18 | <# 19 | .SYNOPSIS 20 | Enumerates the list of mounted graphs as objects. A graph object encapsulates the API scheme and connection (including service endpoints) for accessing a Graph API version. 21 | 22 | .DESCRIPTION 23 | A graph object describes how commands in this module should interact with a specific Graph API version. The Get-Graph command retrieves graph objects and emits them as output. 24 | 25 | For more details on graph objects, including how they can be created and ways in which they are used, see the documentation for the New-Graph command. 26 | 27 | .PARAMETER Name 28 | Every graph has a unique name -- specify the name of the graph through the Name parameter. If a graph with that name exists, it will be returned, otherwise there is no output. 29 | 30 | .OUTPUTS 31 | A graph object encapsulating the API schema, REST request preferences, service endpoints, and authentication / authorization behaviors for accessing a specific Graph API version. If the Name parameter is specified and no graph exists with that name, then there is no output. 32 | 33 | .EXAMPLE 34 | Get-Graph v1.0 35 | 36 | Id : 2bef950e-237e-4273-8094-af8526e8dec3 37 | Endpoint : https://graph.microsoft.com/ 38 | Version : v1.0 39 | CurrentLocation : / 40 | AuthEndpoint : https://login.microsoftonline.com/ 41 | Metadata : Ready 42 | CreationTime : 10/11/2021 6:22:07 PM 43 | LastUpdateTime : 10/11/2021 6:22:07 PM 44 | LastTypeMetadataSource : https://graph.microsoft.com/v1.0/$metadata 45 | 46 | In this example, the name parameter is specified by position with the value "v1.0". The command returns the object corresponding to the graph with the supplied name. 47 | 48 | .EXAMPLE 49 | Get-Graph | Select-Object Name 50 | 51 | Name 52 | ---- 53 | beta 54 | beta_fordev 55 | v1.0 56 | v1_forwork 57 | 58 | When the command is executed without parameters, it returns all named graphs 59 | 60 | .LINK 61 | New-Graph 62 | Remove-Graph 63 | Update-GraphMetadata 64 | #> 65 | function Get-Graph { 66 | [cmdletbinding(positionalbinding=$false)] 67 | param( 68 | [parameter(parametersetname='byname', position=0)] 69 | $Name = $null, 70 | 71 | [parameter(parametersetname='current', mandatory=$true)] 72 | [Switch] $Current 73 | ) 74 | 75 | Enable-ScriptClassVerbosePreference 76 | 77 | $targetGraph = if ( $Current.IsPresent ) { 78 | ($::.GraphContext |=> GetCurrent).name 79 | } else { 80 | $Name 81 | } 82 | 83 | $graphContexts = $::.LogicalGraphManager |=> Get |=> GetContext 84 | 85 | $result = $graphContexts | 86 | where { ! $targetGraph -or $_.name -eq $targetGraph } | foreach { 87 | $::.ContextHelper |=> ToPublicContext $_ 88 | } | sort-object Name 89 | 90 | if ( $Name -and ! $result ) { 91 | throw "The specified Graph '$Name' does not exist." 92 | } 93 | 94 | $result 95 | } 96 | 97 | $::.ParameterCompleter |=> RegisterParameterCompleter Get-Graph Name (new-so GraphParameterCompleter) 98 | -------------------------------------------------------------------------------- /src/cmdlets/Get-Graph.tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | Describe 'The Get-Graph cmdlet' { 17 | BeforeEach { 18 | Get-Graph | Remove-Graph -warningaction silentlycontinue 3>&1 | out-null 19 | } 20 | 21 | AfterEach { 22 | Get-Graph | Remove-Graph -warningaction silentlycontinue 3>&1 | out-null 23 | } 24 | 25 | Context 'When invoking the Get-Graph command' { 26 | It "Should successfully return the default if there is no other graph mounted" { 27 | Get-Graph | Select -expandproperty name | Should Be 'v1.0' 28 | } 29 | 30 | It "Should throw an exception if a the graph with the specified name does not exist" { 31 | { Get-Graph idontexist } | Should Throw 32 | } 33 | 34 | It "Should return a new graph if it is added" { 35 | $newGraph = new-graph NewGraph1 36 | 37 | $newGraph.Name | Should be NewGraph1 38 | 39 | Get-Graph $newGraph.Name | select -expandproperty Name | Should be $newGraph.Name 40 | } 41 | 42 | It "Should throw an exception if a graph is removed by name and immediately specified by name" { 43 | $newGraph = new-graph NewGraphRemoved 44 | 45 | { Get-Graph $newGraph.Name } | Should Not Throw 46 | 47 | Remove-Graph $newGraph.Name 48 | 49 | { Get-Graph $newGraph.Name } | Should Throw 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/cmdlets/Get-GraphLastOutput.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | function Get-GraphLastOutput { 16 | [cmdletbinding(positionalbinding=$false, defaultparametersetname='all')] 17 | param( 18 | [parameter(position=0, parametersetname='index', mandatory=$true)] 19 | [int] $Index, 20 | 21 | [parameter(parametersetname='last', mandatory=$true)] 22 | [int] $Last, 23 | 24 | [parameter(parametersetname='first', mandatory=$true)] 25 | [int] $First, 26 | 27 | [Switch] $ContentOnly, 28 | 29 | [ArgumentCompleter({ 30 | param ( $commandName, 31 | $parameterName, 32 | $wordToComplete, 33 | $commandAst, 34 | $fakeBoundParameters ) 35 | if ( get-variable LASTGRAPHITEMS -erroraction ignore ) { 36 | if ( $LASTGRAPHITEMS -and ($LASTGRAPHITEMS | measure-object).count -gt 0 ) { 37 | $target = if ( $LASTGRAPHITEMS[0].pstypenames -contains 'GraphSegmentDisplayType' -and ( $LASTGRAPHITEMS[0] | gm content -erroraction ignore ) ) { 38 | $LASTGRAPHITEMS[0].Content 39 | } else { 40 | $LASTGRAPHITEMS 41 | } 42 | $target | get-member -membertype noteproperty | where name -like "$wordToComplete*" | foreach { $_.name } 43 | } 44 | } 45 | })] 46 | [string[]] $Property 47 | ) 48 | 49 | $parameterset = $pscmdlet.parametersetname 50 | 51 | $lastItemsVariable = get-variable LASTGRAPHITEMS -erroraction ignore 52 | 53 | if ( $lastItemsVariable -and ( $lastItemsVariable.value | measure-object ).count -gt 0 ) { 54 | $lastResults = $lastItemsVariable.Value 55 | $resultCount = ( $lastResults | measure-object ).count 56 | 57 | $startIndex = 0 58 | $lastIndex = $resultCount - 1 59 | 60 | if ( $parameterset -eq 'index' ) { 61 | $explicitIndex = $true 62 | $startIndex = $Index 63 | $lastIndex = $Index 64 | } elseif ( $parameterset -eq 'last' ) { 65 | $startIndex = $resultCount - $Last 66 | } elseif ( $parameterset -eq 'first' ) { 67 | $lastIndex = $First - 1 68 | } elseif ( $parameterset -ne 'all' ) { 69 | throw "Unexpected parameter set '$parameterset' encountered" 70 | } 71 | 72 | $isContent = ! $lastResults[0].pstypenames.contains('GraphSegmentDisplayType') 73 | $singleElement = ( $lastIndex - $startIndex ) -le 1 74 | 75 | for ( $currentResult = $startIndex; $currentResult -le $lastIndex; $currentResult++ ) { 76 | $content = if ( $isContent -or ! ( $lastResults[0] | gm content -erroraction ignore ) -or ! ( $lastResults[0].Content ) ) { 77 | $lastResults[$currentResult] 78 | } else { 79 | $lastResults[$currentResult].Content 80 | } 81 | 82 | $output = if ( ! $Property ) { 83 | $content 84 | } else { 85 | $content | select-object @Property 86 | } 87 | 88 | $result = if ( ! $ContentOnly.IsPresent -and ! $singleElement ) { 89 | $indexedObject = [PSCustomObject] @{ 90 | Index = $currentResult 91 | Content = $output 92 | } 93 | $indexedObject.pstypenames.insert(0, 'GraphLastResultType') 94 | $indexedObject 95 | } else { 96 | $output 97 | } 98 | 99 | $result 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/cmdlets/Get-GraphLocation.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script ../metadata/SegmentParser) 16 | . (import-script common/LocationHelper) 17 | 18 | <# 19 | .SYNOPSIS 20 | Outputs the current Graph API URI location used by commands as the default URI for issuing Graph API requests or inspecting Graph API metadata. 21 | 22 | .DESCRIPTION 23 | The Get-GraphLocation command gets the current Graph API URI location to a specified value. This current location is used by commands to resolve Uris relative to that location to reduce the data entry overhead of entering long URIs from the console. Maintaining a current location also helps remove the need to remember entire URI paths at every command invocation -- just invoke the command once to set the current location; if subsequent commands use URIs relative to that location, that prefixed location in the URI may be omitted. 24 | 25 | See the documentation for the Set-GraphLocation for more information about the current location concept. 26 | 27 | Get-GraphLocation provides the alias 'gwd' because the behavior of Get-GraphLocation with regard to Graph API URIs is analgous to the functionalty of the 'pwd' command in many shell languages used to get the current 'working directory', i.e. set the current location in the file system. 28 | 29 | The current location may also be displayed in the PowerShell prompt if prompt integration has not been disabled using the Set-GraphPrompt command or related profile settings. 30 | 31 | .OUTPUTS 32 | This command outputs an object with a property Path that has the value of the current location. This location is fully qualified, meaning its first segment is of the form /:, where graphname is the name of a currently mounted graph. See the documentation Set-GraphLocation for more information on the format of graph URIs. 33 | 34 | .EXAMPLE 35 | gwd 36 | 37 | Path 38 | ---- 39 | /v1.0:/me/drive/root/children 40 | 41 | In this example, Get-GraphLocation was invoked using its alias 'gwd' to output the current location, /v1.0:/me/drive/root/children. 42 | 43 | .EXAMPLE 44 | gcd -TypeName user 45 | gwd 46 | 47 | Path 48 | ---- 49 | /beta:/users 50 | 51 | Here the Set-GraphLocation command is invoked via its 'gcd' alias to change the current location to the default URI for the 'user' type. Get-GraphLocation, in this case invoked by its 'gwd' alias, can then be used to inspect the current location which shows the path to be /beta:/users. 52 | 53 | .LINK 54 | Set-GraphLocation 55 | Get-GraphResourceWithMetadata 56 | Get-GraphResource 57 | Get-GraphLastOutput 58 | Set-GraphPrompt 59 | Get-GraphType 60 | #> 61 | function Get-GraphLocation { 62 | [cmdletbinding()] 63 | param() 64 | 65 | Enable-ScriptClassVerbosePreference 66 | 67 | $context = $::.GraphContext |=> GetCurrent 68 | 69 | if ( ! $context ) { 70 | throw "Cannot get location in the current context because no current context exists" 71 | } 72 | 73 | $parser = new-so SegmentParser $context $null $true 74 | 75 | $::.LocationHelper |=> ToPublicLocation $parser $context.location 76 | } 77 | -------------------------------------------------------------------------------- /src/cmdlets/Get-GraphMethod.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script common/MethodDisplayType) 16 | . (import-script common/TypeMemberFinder) 17 | . (import-script common/TypeUriParameterCompleter) 18 | . (import-script common/MemberParameterCompleter) 19 | 20 | function Get-GraphMethod { 21 | [cmdletbinding(positionalbinding=$false, defaultparametersetname='uri')] 22 | [OutputType('MethodDisplayType')] 23 | param( 24 | [parameter(position=0, parametersetname='fortypename', mandatory=$true)] 25 | [parameter(parametersetname='fortypenamepipeline', valuefrompipelinebypropertyname=$true, mandatory=$true)] 26 | [Alias('TypeId')] 27 | $TypeName, 28 | 29 | [parameter(position=1)] 30 | [Alias('MethodName')] 31 | [string] $Name, 32 | 33 | [parameter(parametersetname='uri', mandatory=$true)] 34 | [Alias('GraphUri')] 35 | $Uri, 36 | 37 | [parameter(parametersetname='fortypenamepipeline', valuefrompipelinebypropertyname=$true, mandatory=$true)] 38 | [parameter(parametersetname='forobject')] 39 | [parameter(parametersetname='uri')] 40 | $GraphName, 41 | 42 | [parameter(parametersetname='forobject', valuefrompipeline=$true, mandatory=$true)] 43 | [PSTypeName('GraphResponseObject')] $GraphItem, 44 | 45 | [switch] $FullyQualifiedTypeName, 46 | 47 | [switch] $Parameters, 48 | 49 | [string] $MethodFilter 50 | ) 51 | 52 | begin { 53 | Enable-ScriptClassVerbosePreference 54 | } 55 | 56 | process { 57 | $forwardedParameters = @{MemberType='Method'} 58 | 59 | 'Name', 'TypeName', 'Uri', 'GraphName', 'GraphItem', 'FullyQualifiedTypeName' | foreach { 60 | if ( $PSBoundParameters.ContainsKey($_) ) { 61 | $forwardedParameters.Add($_, $PSBoundParameters[$_]) 62 | } 63 | } 64 | 65 | $targetType = $TypeName 66 | 67 | $isMethodUri = $false 68 | 69 | if ( $Uri ) { 70 | $uriInfo = Get-GraphUriInfo -Uri $Uri 71 | $isMethodUri = $uriInfo.Class -in 'Action', 'Function' 72 | if ( $isMethodUri ) { 73 | if ( $Name ) { 74 | throw [ArgumentException]::new('The Graph location URI parameter specified a method, so the method Name parameter may not also be specified') 75 | } 76 | $forwardedParameters['Uri'] = $uriInfo.ParentPath 77 | $forwardedParameters['Name'] = $uriInfo.Name 78 | $forwardedParameters.Add('StrictUri', [System.Management.Automation.SwitchParameter]::new($true)) 79 | } 80 | } 81 | 82 | if ( $GraphItem ) { 83 | $objectUriInfo = Get-GraphUriInfo -GraphItem $GraphItem 84 | $targetType = $objectUriInfo.__ItemMetadata().TypeId 85 | } 86 | 87 | $methods = Get-GraphMember @forwardedParameters 88 | 89 | if ( $isMethodUri ) { 90 | $targetType = $methods.DefiningTypeId 91 | } 92 | 93 | foreach ( $method in $methods ) { 94 | if ( ! $Parameters.IsPresent ) { 95 | new-so MethodDisplayType $method $targetType 96 | } else { 97 | $::.MethodDisplayType |=> ToPublicParameterList $method 98 | } 99 | } 100 | } 101 | 102 | end { 103 | } 104 | } 105 | 106 | $::.ParameterCompleter |=> RegisterParameterCompleter Get-GraphMethod TypeName (new-so TypeParameterCompleter) 107 | $::.ParameterCompleter |=> RegisterParameterCompleter Get-GraphMethod GraphName (new-so GraphParameterCompleter) 108 | $::.ParameterCompleter |=> RegisterParameterCompleter Get-GraphMethod Uri (new-so GraphUriParameterCompleter LocationOrMethodUri) 109 | $::.ParameterCompleter |=> RegisterParameterCompleter Get-GraphMethod Name (new-so MemberParameterCompleter TypeName $null Method) 110 | -------------------------------------------------------------------------------- /src/cmdlets/Get-GraphResourceWithMetadata.tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # This test assumes the module has been imported 16 | 17 | $expectedUserPrincipalName = 'searchman@megarock.org' 18 | $meResponseDataExpected = '"@odata.context":"https://graph.microsoft.com/v1.0/$metadata#users/$entity","businessPhones":[],"displayName":"Search Man","givenName":null,"jobTitle":"Administrator","mail":null,"mobilePhone":null,"officeLocation":null,"preferredLanguage":null,"surname":null,"userPrincipalName":"{0}","id":"012345567-89ab-cdef-0123-0123456789ab"' -f $expectedUserPrincipalName 19 | $meResponseExpected = "{$meResponseDataExpected}" 20 | 21 | Describe 'The Get-GraphResourceWithMetadata cmdlet' { 22 | Context 'When making REST method calls to Graph' { 23 | BeforeAll { 24 | $progresspreference = 'silentlycontinue' 25 | Update-GraphMetadata -Path "$psscriptroot/../../test/assets/microsoft-directoryservices-fragment.xml" -force -wait -warningaction silentlycontinue 26 | } 27 | 28 | ScriptClass MockToken { 29 | function CreateAuthorizationHeader {} 30 | } 31 | 32 | $mockScript = @" 33 | `$ItemContextScript = [ScriptBlock]::Create("[PSCustomObject] @{RequestUri=``"https://graph.microsoft.com/v1.0/me``";IsEntity=```$true;IsDelta=```$false;IsCollectionMember=```$false;TypelessGraphUri=``"/Users``"}") 34 | `$responseObject = '{$meResponseDataExpected}' | convertfrom-json 35 | `$responseObject | add-member -membertype scriptmethod -name __ItemContext -value `$ItemContextScript 36 | `$responseObject 37 | "@ 38 | 39 | # Variables from the script can't be used in the scriptblock if modulename is used. So we will dynamically 40 | # create a script block -- sigh. :( 41 | Mock Invoke-GraphApiRequest ([ScriptBlock]::Create($mockScript)) -modulename autographps 42 | 43 | Add-MockInScriptClassScope RESTRequest Invoke-WebRequest { 44 | [PSCustomObject] @{ 45 | RawContent = $MockContext 46 | RawContentLength = $MockContext.length 47 | Content = $MockContext 48 | StatusCode = 200 49 | StatusDescription = 'OK' 50 | Headers = @{'Content-Type'='application/json'} 51 | } 52 | } -MockContext @{ 53 | expectedUserPrincipalName = $expectedUserPrincipalName 54 | meResponseDataExpected = $meResponseDataExpected 55 | meResponseExpected = $meResponseExpected 56 | } 57 | 58 | It 'Should return an object with the expected user principal when given the argument me' { 59 | $meResponse = Get-GraphResourceWithMetadata me 3> $null 60 | $meResponse.userPrincipalName | Should Be $expectedUserPrincipalName 61 | $meResponse.__ItemMetadata().Preview | Should Be 'Search Man' 62 | } 63 | } 64 | } 65 | 66 | -------------------------------------------------------------------------------- /src/cmdlets/Get-GraphUri.tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | set-strictmode -version 2 16 | 17 | Describe 'The Get-GraphUriInfo command' -tag testnow { 18 | Context 'When invoked using v1 metadata with namespace aliases' { 19 | BeforeAll { 20 | $progresspreference = 'silentlycontinue' 21 | Update-GraphMetadata -Path "$psscriptroot/../../test/assets/v1metadata-ns-alias-2020-01-22.xml" -force -wait -warningaction silentlycontinue 22 | } 23 | 24 | It "Should successfully return child segments of the 'me' singleton of entity type 'user' including actions and functions" { 25 | $progresspreference = 'silentlycontinue' 26 | 27 | $propertiesToValidate = 'Class', 'Collection', 'Endpoint', 'FullTypeName', 'GraphUri', 'Id', 'Info', 'IsDynamic', 'Namespace', 'ParentPath', 'Path', 'Preview', 'PSTypeName', 'Relation', 'Type', 'AbsoluteUri', 'Version' 28 | 29 | $children = Get-GraphUriInfo /me -children | select Class, Collection, Endpoint, FullTypeName, GraphUri, Id, Info, IsDynamic, Namespace, ParentPath, Path, Preview, PSTypeName, Relation, Type, AbsoluteUri, Version 30 | 31 | 32 | # Use this command to generate the file: 33 | # Get-GraphUriInfo /me -Children | sort Id | select 'Class', 'Collection', 'Endpoint', 'FullTypeName', 'GraphUri', 'Id', 'Info', 'IsDynamic', 'Namespace', 'ParentPath', 'Path', 'Preview', 'PSTypeName', 'Relation', 'Type', 'AbsoluteUri', 'Version' | convertto-json -Depth 1 34 | $GetGraphUriMeResult = get-content $psscriptroot/../../test/assets/GetGraphUriMe.json 35 | 36 | $expectedChildren = $GetGraphUriMeResult | ConvertFrom-Json 37 | 38 | $children.length | Should Be $expectedChildren.length 39 | 40 | for ( $index = 0; $index -lt $children.length; $index++ ) { 41 | foreach ( $property in $propertiesToValidate ) { 42 | $result = $children[$index] | select -expandproperty $property 43 | $expect = $expectedChildren[$index] | select -expandproperty $property 44 | $result | Should Be $expect 45 | } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/cmdlets/New-GraphObject.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script ../typesystem/TypeManager) 16 | . (import-script common/TypeParameterCompleter) 17 | . (import-script common/TypePropertyParameterCompleter) 18 | 19 | function New-GraphObject { 20 | [cmdletbinding(positionalbinding=$false, defaultparametersetname='optionallyqualified')] 21 | param( 22 | [parameter(position=0, mandatory=$true)] 23 | $TypeName, 24 | 25 | [ValidateSet('Any', 'Primitive', 'Enumeration', 'Complex', 'Entity')] 26 | $TypeClass = 'Any', 27 | 28 | [parameter(position=1, parametersetname='optionallyqualified')] 29 | [parameter(position=1, parametersetname='fullyqualified')] 30 | [string[]] $Property, 31 | 32 | [parameter(position=2, parametersetname='optionallyqualified')] 33 | [parameter(position=2, parametersetname='fullyqualified')] 34 | [object[]] $Value, 35 | 36 | $GraphName, 37 | 38 | [parameter(parametersetname='optionallyqualifiedpropmap', mandatory=$true)] 39 | [parameter(parametersetname='fullyqualifiedpropmap', mandatory=$true)] 40 | $PropertyTable, 41 | 42 | [parameter(parametersetname='fullyqualified', mandatory=$true)] 43 | [parameter(parametersetname='fullyqualifiedpropmap', mandatory=$true)] 44 | [switch] $FullyQualifiedTypeName, 45 | 46 | [switch] $Json, 47 | 48 | [switch] $Recurse, 49 | 50 | [switch] $SetDefaultValues, 51 | 52 | [switch] $SkipPropertyCheck 53 | ) 54 | 55 | Enable-ScriptClassVerbosePreference 56 | 57 | $targetContext = $::.ContextHelper |=> GetContextByNameOrDefault $GraphName 58 | 59 | $typeManager = $::.TypeManager |=> Get $targetContext 60 | 61 | $isFullyQualified = $FullyQualifiedTypeName.IsPresent -or ( $typeClass -ne 'Primitive' -and $TypeName.Contains('.') ) 62 | 63 | $remappedTypeClass = if ( $TypeClass -ne 'Any' ) { 64 | $TypeClass 65 | } else { 66 | 'Unknown' 67 | } 68 | 69 | if ( $Value -and ! $Property ) { 70 | throw [ArgumentException]::('When the Value parameter is specified, the property parameter must also be specified') 71 | } 72 | 73 | if ( $Value ) { 74 | $valueLength = ( $Value | measure-object ).count 75 | $propertyLength = ( $Property | measure-object ).count 76 | if ( $valueLength -gt $propertyLength ) { 77 | throw [ArgumentException]::("The specified Value parameter's length of $ValueLength must be less than the specified Property parameter's length of $propertyLength") 78 | } 79 | } 80 | 81 | $prototype = $typeManager |=> GetPrototype $remappedTypeClass $TypeName $isFullyQualified $SetDefaultValues.IsPresent $Recurse.IsPresent $Property $Value $PropertyTable $SkipPropertyCheck.IsPresent 82 | 83 | $prototypeJson = $prototype.ObjectPrototype | convertto-json -depth 24 84 | 85 | if ( $Json.IsPresent ) { 86 | $prototypeJSON 87 | } else { 88 | $resultObject = $prototypeJSON | convertfrom-json 89 | $::.TypeUriHelper |=> DecorateObjectWithType $resultObject $prototype.Type 90 | $resultObject 91 | } 92 | } 93 | 94 | $::.ParameterCompleter |=> RegisterParameterCompleter New-GraphObject TypeName (new-so TypeParameterCompleter) 95 | $::.ParameterCompleter |=> RegisterParameterCompleter New-GraphObject Property (new-so TypePropertyParameterCompleter) 96 | $::.ParameterCompleter |=> RegisterParameterCompleter New-GraphObject GraphName (new-so GraphParameterCompleter) 97 | -------------------------------------------------------------------------------- /src/cmdlets/Remove-Graph.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script common/GraphParameterCompleter) 16 | 17 | <# 18 | .SYNOPSIS 19 | Removes a mounted graph so it is no longer accessible. 20 | 21 | .DESCRIPTION 22 | A graph object describes how commands in this module should interact with a specific Graph API version. Such objects are "mounted" which makes them available for use with commands. The Remove-Graph command removes mounted graph objects making them unavailable for use with commands. 23 | 24 | For more details on graph objects, including how they can be created and ways in which they are used, see the documentation for the New-Graph command. 25 | 26 | .PARAMETER Name 27 | Every graph has a unique name -- specify the name of the graph through the Name parameter. If a graph with name specifiec by this parameter exists it will be removed, otherwise the command results in an error. 28 | 29 | .OUTPUTS 30 | None. 31 | 32 | .EXAMPLE 33 | 34 | Remove-Graph beta 35 | 36 | In this example, the beta graph is removed.name parameter is specified by position with the value "v1.0". The command returns the object corresponding to the graph with the supplied name. 37 | 38 | .EXAMPLE 39 | Get-Graph beta | Remove-Graph 40 | 41 | This example is similar to the previous example, but in this case the graph to remove is piped in from the result of a previous invocation to Get-Graph. 42 | 43 | .LINK 44 | Get-Graph 45 | New-Graph 46 | #> 47 | function Remove-Graph { 48 | [cmdletbinding(positionalbinding=$false)] 49 | param( 50 | [parameter(position=0, valuefrompipelinebypropertyname=$true, mandatory=$true)] 51 | [string] $Name 52 | ) 53 | 54 | begin { 55 | Enable-ScriptClassVerbosePreference 56 | } 57 | 58 | process { 59 | # Seems that if you accept pipeline input you can't rely on a mandatory parameter being non-null / non-empty 60 | # This likely preserves the output to accept empty results as inputs without throwing an exception. 61 | if ( $Name ) { 62 | $::.LogicalGraphManager |=> Get |=> RemoveContext $Name 63 | } 64 | } 65 | 66 | end { 67 | } 68 | } 69 | 70 | $::.ParameterCompleter |=> RegisterParameterCompleter Remove-Graph Name (new-so GraphParameterCompleter) 71 | -------------------------------------------------------------------------------- /src/cmdlets/Update-GraphMetadata.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script ../metadata/GraphManager) 16 | 17 | function Update-GraphMetadata { 18 | [cmdletbinding(positionalbinding=$false, defaultparametersetname='GraphTarget')] 19 | param( 20 | [parameter(position=0, parametersetname='Path', mandatory=$true)] 21 | $Path = $null, 22 | 23 | [parameter(valuefrompipelinebypropertyname=$true, parametersetname='GraphTarget')] 24 | [Alias('Name')] 25 | [String] 26 | $GraphName, 27 | 28 | [parameter(parametersetname='Data', valuefrompipeline=$true)] 29 | [String] $SchemaData, 30 | 31 | [switch] $Force, 32 | 33 | [switch] $Wait 34 | ) 35 | 36 | Enable-ScriptClassVerbosePreference 37 | 38 | $metadata = if ( $Path ) { 39 | [xml] (get-content $Path | out-string) 40 | } elseif ( $SchemaData ) { 41 | [xml] $SchemaData 42 | } 43 | 44 | $context = if ( $GraphName ) { 45 | $::.LogicalGraphManager |=> Get |=> GetContext $GraphName 46 | } else { 47 | $::.GraphContext |=> GetCurrent 48 | } 49 | 50 | $::.GraphManager |=> UpdateGraph $context $metadata $wait.ispresent $force.ispresent $Path 51 | } 52 | -------------------------------------------------------------------------------- /src/cmdlets/common/ContextHelper.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script ../../metadata/GraphManager) 16 | 17 | ScriptClass ContextHelper { 18 | static { 19 | const ContextDisplayTypeName 'GraphContextDisplayType' 20 | 21 | function __initialize { 22 | __RegisterContextDisplayType 23 | } 24 | 25 | function ToPublicContext( $context ) { 26 | $userInfo = $context.connection.identity |=> GetUserInformation 27 | 28 | $userId = $userInfo.userId 29 | $scopes = $userInfo.scopes 30 | 31 | $timeData = $context |=> GetState $::.GraphManager.TimeStateKey 32 | $creationTime = if ( $timeData ) { $timeData.CreatedTime } 33 | $lastUpdateTime = if ( $timeData ) { $timeData.UpdatedTime } 34 | 35 | # Seems like ScriptClass constants have a strange behavior when used as a typename here. 36 | # To work around this, use ToString() 37 | [PSCustomObject]@{ 38 | PSTypeName = ($this.ContextDisplayTypeName.tostring()) 39 | Id = $context.Id 40 | Name = $context.name 41 | Version = $context.version 42 | Endpoint = ($context |=> GetEndpoint) 43 | AuthEndpoint = $context.connection.GraphEndpoint.Authentication 44 | CurrentLocation = $context.location 45 | Authenticated = $context.connection.Connected 46 | Metadata = $::.GraphManager |=> GetMetadataStatus $context 47 | ConnectionStatus = $context.connection.status 48 | ApplicationId = $context.connection.identity.app.appid 49 | TenantId = $context.connection.identity.TenantDisplayId 50 | TenantName = $context.connection.identity.TenantDisplayName 51 | AuthType = $context.connection.identity.app.authtype 52 | UserId = $userId 53 | Scopes = $scopes 54 | CreationTime = $creationTime 55 | LastUpdateTime = $lastUpdateTime 56 | LastTypeMetadataSource = $context |=> GetState $::.GraphManager.MetadataSourceStateKey 57 | Details = $context 58 | } 59 | 60 | } 61 | 62 | function GetContextByNameOrDefault($contextName) { 63 | if ( $contextName ) { 64 | $::.LogicalGraphManager |=> Get |=> GetContext $contextName 65 | } else { 66 | $::.GraphContext |=> GetCurrent 67 | } 68 | } 69 | 70 | function __RegisterContextDisplayType { 71 | remove-typedata -typename $this.ContextDisplayTypeName -erroraction ignore 72 | 73 | $coreProperties = @('Metadata', 'Endpoint', 'Version', 'Name') 74 | 75 | $contextDisplayTypeArguments = @{ 76 | TypeName = $this.ContextDisplayTypeName 77 | MemberType = 'NoteProperty' 78 | MemberName = 'PSTypeName' 79 | Value = $this.ContextDisplayTypeName 80 | DefaultDisplayPropertySet = $coreProperties 81 | } 82 | 83 | Update-TypeData -force @contextDisplayTypeArguments 84 | } 85 | 86 | } 87 | } 88 | 89 | $::.ContextHelper |=> __initialize 90 | -------------------------------------------------------------------------------- /src/cmdlets/common/FunctionParameterHelper.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ScriptClass FunctionParameterHelper { 16 | static { 17 | function ToUriParameterString($parameterObject) { 18 | $parameterNames = $null 19 | $values = $null 20 | 21 | if ( $parameterObject -is [HashTable] ) { 22 | $parameterNames = $parameterObject.keys 23 | $values = $parameterObject.Values 24 | } else { 25 | $parameterNames = $parameterObject | gm -membertype -noteproperty | select -expandproperty name 26 | $values = $parameterNames | foreach { $parameterObject.$_ } 27 | } 28 | 29 | $parameters = if ( $parameterNames ) { 30 | $valueIndex = 0 31 | foreach ( $parameterName in $parameterNames ) { 32 | $parameterJson = $values[$valueIndex++] | convertto-json -depth 1 33 | $parameterValue = $parameterJson -replace '"', "'" 34 | "$parameterName = $parameterValue" 35 | } 36 | } 37 | 38 | '({0})' -f $parameters -join ', ' 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/cmdlets/common/FunctionSegmentBuilder.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ScriptClass FunctionSegmentBuilder { 16 | $functionName = $null 17 | 18 | function __initialize($functionName) { 19 | if ( ! $this.functionName ) { 20 | throw [ArgumentException]::new('A method name was not specified') 21 | } 22 | $this.functionName = $functionName 23 | } 24 | 25 | static { 26 | function ToUriParameterString($parameterObject) { 27 | $parameterNames = $null 28 | $values = $null 29 | 30 | if ( $parameterObject -is [HashTable] ) { 31 | $parameterNames = $parameterObject.keys 32 | $values = $parameterObject.Values 33 | } else { 34 | $parameterNames = $parameterObject | gm -membertype -noteproperty | select -expandproperty name 35 | $values = $parameterNames | foreach { $parameterObject.$_ } 36 | } 37 | 38 | $parameters = if ( $parameterNames ) { 39 | $valueIndex = 0 40 | foreach ( $parameterName in $parameterNames ) { 41 | $parameterJson = $values[$valueIndex++] | convertto-json -depth 1 42 | $parameterValue = $parameterJson -replace '"', "'" 43 | "$parameterName = $parameterValue" 44 | } 45 | } 46 | 47 | '({0})' -f $parameters -join ', ' 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/cmdlets/common/GraphParameterCompleter.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2019, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ScriptClass GraphParameterCompleter { 16 | function CompleteCommandParameter { 17 | param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) 18 | $contexts = $::.LogicalGraphManager |=> Get |=> GetContext | where { 19 | $_.name.StartsWith($wordToComplete, [System.StringComparison]::InvariantCultureIgnoreCase) 20 | } 21 | 22 | if ( $contexts ) { 23 | $contexts | select -expandproperty name 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/cmdlets/common/GraphStatisticsDisplayType.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ScriptClass GraphStatisticsDisplayType { 16 | # Note that the display format for this type is managed 17 | # by a format xml file elsewhere in this module rather 18 | # than directly by this class 19 | 20 | $EntityTypeCount = $null 21 | $EntityPropertyCount = $null 22 | $EntityRelationshipCount = $null 23 | $EntityMethodCount = $null 24 | $EnumerationTypeCount = $null 25 | $EnumerationValueCount = $null 26 | $ComplexTypeCount = $null 27 | $ComplexPropertyCount = $null 28 | $EnumerationTypeCount = $null 29 | $EnumerationValueCount = $null 30 | $GraphName = $null 31 | 32 | function __initialize($statistics, $graphName) { 33 | $this.EntityTypeCount = $statistics.EntityCount 34 | $this.EntityPropertyCount = $statistics.EntityPropertyCount 35 | $this.ComplexTypeCount = $statistics.ComplexCount 36 | $this.ComplexPropertyCount = $statistics.ComplexPropertyCount 37 | $this.EnumerationTypeCount = $statistics.EnumerationCount 38 | $this.EnumerationValueCount = $statistics.EnumerationValueCount 39 | $this.EntityRelationshipCount = $statistics.EntityNavigationPropertyCount 40 | $this.EntityMethodCount = $statistics.MethodCount 41 | $this.GraphName = $graphName 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /src/cmdlets/common/GraphUriParameterCompleter.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2019, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | enum GraphUriCompletionType { 16 | AnyUri 17 | LocationOrMethodUri 18 | LocationUri 19 | } 20 | 21 | ScriptClass GraphUriParameterCompleter { 22 | $uriCompletionType = $null 23 | $nonLocatable = $false 24 | $includeVirtual = $false 25 | 26 | function __initialize([GraphUriCompletionType] $uriCompletionType) { 27 | $this.uriCompletionType = $uriCompletionType 28 | 29 | switch ($this.uriCompletionType) { 30 | ([GraphUriCompletionType]::AnyUri) { 31 | $this.nonLocatable = $true 32 | $this.includeVirtual = $true 33 | } 34 | ([GraphUriCompletionType]::LocationOrMethodUri) { 35 | $this.nonLocatable = $true 36 | $this.includeVirtual = $false 37 | } 38 | ([GraphUriCompletionType]::LocationUri) { 39 | $this.nonLocatable = $false 40 | $this.includeVirtual = $false 41 | } 42 | } 43 | } 44 | 45 | function CompleteCommandParameter { 46 | param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) 47 | __GetUriCompletions $wordToComplete $this.nonLocatable $this.includeVirtual 48 | } 49 | 50 | function __GetUriCompletions([uri] $targetUri, [bool] $nonLocatable=$false, [bool] $includeVirtual=$false) { 51 | $uriString = $targetUri.tostring() 52 | $lastWord = $uriString -split '/' | select -last 1 53 | 54 | $parentUri = $uriString.substring(0, $uriString.length - $lastword.length).trimend('/') 55 | 56 | if ( ! $parentUri ) { 57 | if ( $uriString.StartsWith('/') ) { 58 | $parentUri = '/' 59 | } else { 60 | $parentUri = '.' 61 | } 62 | } 63 | 64 | $candidateUris = Get-GraphUriInfo $parentUri -children -includevirtualchildren:$includeVirtual -LocatableChildren:(!$nonLocatable) -ignoremissingmetadata 65 | 66 | $fullParent = $null 67 | $completions = if ( $candidateUris ) { 68 | $sample = $candidateUris[0].graphuri.originalstring 69 | $fullParent = $sample.substring(0, $sample.lastindexof('/')) 70 | $candidates = $candidateUris | 71 | select -expandproperty graphuri | 72 | select -expandproperty originalstring | 73 | foreach { 74 | $_ -split '/' | select -last 1 75 | } 76 | 77 | $::.ParameterCompleter |=> FindMatchesStartingWith $lastword $candidates 78 | } 79 | 80 | $completions | foreach { 81 | $fullParent, $_ -join '/' 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/cmdlets/common/LocationHelper.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2019, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script SegmentHelper) 16 | 17 | ScriptClass LocationHelper { 18 | static { 19 | const LocationDisplayTypeName 'GraphLocationDisplayType' 20 | 21 | function __initialize { 22 | __RegisterLocationDisplayType 23 | } 24 | 25 | function ToPublicLocation( $parser, $segment ) { 26 | $publicSegment = $::.SegmentHelper |=> ToPublicSegment $parser $segment 27 | 28 | # The ToString() below is actually required for the PSTypeName member to fulfill its 29 | # function of type conversion 30 | [PSCustomObject]@{ 31 | PSTypeName = $this.LocationDisplayTypeName.tostring() 32 | Path = $publicSegment.Path 33 | Details = $publicSegment 34 | } 35 | } 36 | 37 | function ToLocationUriPath( $context, $relativeUri ) { 38 | $graphRelativeUri = $this.ToGraphRelativeUriPathQualified($relativeUri, $context) 39 | "/{0}:{1}" -f $context.name, $graphRelativeUri 40 | } 41 | 42 | function ToGraphRelativeUriPathQualified( $relativeUri, $context = $null ) { 43 | $unqualifiedPath = $::.GraphUtilities |=> __ToGraphRelativeUriPath $relativeUri $context 44 | __ToQualifiedUri $unqualifiedPath $context 45 | } 46 | 47 | function __ToQualifiedUri($graphRelativeUriString, $context) { 48 | $graph = $::.GraphManager |=> GetGraph $context 49 | $relativeVersionedUriString = $::.GraphUtilities |=> JoinRelativeUri $graph.ApiVersion $graphRelativeUriString 50 | $::.GraphUtilities |=> JoinAbsoluteUri $graph.Endpoint $relativeVersionedUriString 51 | } 52 | 53 | function __RegisterLocationDisplayType { 54 | remove-typedata -typename $this.LocationDisplayTypeName -erroraction ignore 55 | 56 | $coreProperties = @('Path') 57 | 58 | $locationDisplayTypeArguments = @{ 59 | TypeName = $this.LocationDisplayTypeName 60 | MemberType = 'NoteProperty' 61 | MemberName = 'PSTypeName' 62 | Value = $this.LocationDisplayTypeName.tostring() 63 | DefaultDisplayPropertySet = $coreProperties 64 | } 65 | 66 | Update-TypeData -force @locationDisplayTypeArguments 67 | } 68 | 69 | } 70 | } 71 | 72 | $::.LocationHelper |=> __initialize 73 | -------------------------------------------------------------------------------- /src/cmdlets/common/MemberDisplayType.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ScriptClass MemberDisplayType { 16 | $Name = $null 17 | $TypeId = $null 18 | $IsCollection = $false 19 | $MemberType = $null 20 | $Parameters = $null 21 | $MethodType = $null 22 | $DefiningTypeId = $null 23 | $RequestedType = $null 24 | 25 | function __initialize($typeMember, $RequestedType) { 26 | $this.Name = $typeMember.Name 27 | $this.MemberType = $typeMember.MemberType 28 | $this.TypeId = $typeMember.TypeId 29 | $this.IsCollection = $typeMember.IsCollection 30 | $this.DefiningTypeId = $typeMember.DefiningTypeId 31 | $this.RequestedType = $RequestedType 32 | 33 | if ( $typeMember.MemberType -eq 'Method' ) { 34 | $this.MethodType = $typeMember.MemberData.MethodType 35 | $this.Parameters = $typeMember.MemberData.Parameters 36 | 37 | if ( $typeMember.MemberData.ReturnTypeInfo ) { 38 | $this.TypeId = $typeMember.MemberData.ReturnTypeInfo.TypeId 39 | $this.IsCollection = $typeMember.MemberData.ReturnTypeInfo.IsCollection 40 | } 41 | } 42 | } 43 | 44 | static { 45 | function __RegisterDisplayType { 46 | $displayTypeName = $this.ClassName 47 | 48 | $displayProperties = @('Name', 'MemberType', 'TypeId', 'IsCollection') 49 | 50 | remove-typedata -typename $displayTypeName -erroraction ignore 51 | 52 | $displayTypeArguments = @{ 53 | TypeName = $displayTypeName 54 | DefaultDisplayPropertySet = $displayProperties 55 | } 56 | 57 | Update-TypeData -force @displayTypeArguments 58 | } 59 | 60 | function ToDisplayableMemberList($memberList) { 61 | $memberList = foreach ( $member in $memberList ) { 62 | new-so $this.ClassName $member 63 | } 64 | 65 | $memberListResult = $memberList 66 | if ( $memberList -and ($memberList | measure-object).count -eq 1 ) { 67 | $memberListResult = , $memberList 68 | } 69 | [PSCustomObject] @{Result = $memberListResult} 70 | } 71 | } 72 | } 73 | 74 | 75 | $::.MemberDisplayType |=> __RegisterDisplayType 76 | -------------------------------------------------------------------------------- /src/cmdlets/common/MemberParameterCompleter.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ScriptClass MemberParameterCompleter { 16 | $typeNameParameter = $null 17 | $memberTypeParameter = $null 18 | $memberType = $null 19 | 20 | function __initialize( $typeNameParameter, $memberTypeParameter, $memberType ) { 21 | $this.typeNameParameter = $typeNameParameter 22 | $this.memberTypeParameter = $memberTypeParameter 23 | $this.memberType = $memberType 24 | } 25 | 26 | function CompleteCommandParameter { 27 | param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) 28 | 29 | $graphName = $fakeBoundParameters['GraphName'] 30 | $typeName = $fakeBoundParameters[$this.typeNameParameter] 31 | $memberType = if ( $this.memberType ) { 32 | $memberType 33 | } else { 34 | $fakeBoundParameters[$this.memberTypeParameter] 35 | } 36 | 37 | $targetContext = $::.ContextHelper |=> GetContextByNameOrDefault $graphName 38 | 39 | $memberFilter = '{0}*' -f $wordToComplete 40 | 41 | $members = $::.TypeMemberFinder |=> FindMembersByTypeName $targetContext $typeName $memberType $null $memberFilter $null $true 42 | 43 | if ( $members ) { 44 | $members | select-object -expandproperty Name 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/cmdlets/common/MethodDisplayType.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ScriptClass MethodDisplayType { 16 | $Name = $null 17 | $MethodType = $null 18 | $ReturnType = $null 19 | $Parameters = $null 20 | $DefiningTypeId = $null 21 | $RequestedType = $null 22 | 23 | function __initialize($typeMethod, $requestedType) { 24 | $this.Name = $typeMethod.Name 25 | $this.MethodType = $typeMethod.MethodType 26 | $this.ReturnType = [PSCUstomObject] @{ 27 | TypeId = $typeMethod.TypeId 28 | IsCollection = $typeMethod.IsCollection 29 | } 30 | $this.Parameters = $typeMethod.Parameters 31 | $this.DefiningTypeId = $typeMethod.DefiningTypeId 32 | $this.RequestedType = $requestedType 33 | } 34 | 35 | static { 36 | function __RegisterDisplayType { 37 | $displayTypeName = $this.ClassName 38 | 39 | $displayProperties = @('Name', 'MethodType', 'ReturnType', 'Parameters') 40 | 41 | remove-typedata -typename $displayTypeName -erroraction ignore 42 | 43 | $displayProperties | foreach { 44 | $memberArgs = @{ 45 | TypeName = $displayTypeName 46 | MemberType = 'NoteProperty' 47 | MemberName = $_ 48 | Value = $null 49 | } 50 | 51 | Update-TypeData -force @memberArgs 52 | } 53 | 54 | $displayTypeArguments = @{ 55 | TypeName = $displayTypeName 56 | DefaultDisplayPropertySet = $displayProperties 57 | } 58 | 59 | Update-TypeData -force @displayTypeArguments 60 | } 61 | 62 | function ToPublicParameterList($method) { 63 | if ( $method.parameters ) { 64 | $result = $method.parameters | foreach { 65 | $newParameter = [PSCustomObject] @{ 66 | MethodName = $method.Name 67 | Name = $_.Name 68 | TypeId = $_.TypeId 69 | IsCollection = $_.IsCollection 70 | } 71 | $newParameter.pstypenames.insert(0, 'MethodParameterType') 72 | $newParameter 73 | } 74 | 75 | $result 76 | } 77 | } 78 | } 79 | } 80 | 81 | 82 | $::.MethodDisplayType |=> __RegisterDisplayType 83 | -------------------------------------------------------------------------------- /src/cmdlets/common/MethodNameParameterCompleter.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ScriptClass MethodNameParameterCompleter { 16 | function CompleteCommandParameter { 17 | param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) 18 | $graphName = $fakeBoundParameters['GraphName'] 19 | $typeName = $fakeBoundParameters['TypeName'] 20 | $fullyQualified = if ( $fakeBoundParameters['FullyQualifiedTypeName'] ) { 21 | $fakeBoundParameters['FullyQualifiedTypeName'].IsPresent 22 | } else { 23 | $typeName -ne $null -and $typeName.Contains('.') 24 | } 25 | 26 | if ( ! $typeName ) { 27 | return $null 28 | } 29 | 30 | $targetContext = $::.ContextHelper |=> GetContextByNameOrDefault $graphName 31 | 32 | if ( $targetContext ) { 33 | $typeManager = $::.TypeManager |=> Get $targetContext 34 | $isFullyQualified = $fullyQualified -or ( $typeName.Contains('.') ) 35 | 36 | $methods = $::.TypeMemberFinder |=> FindMembersByTypeName $targetContext $typeName Method $null $null $null 37 | 38 | if ( $methods ) { 39 | $methods.Name | where { $_.StartsWith($wordToComplete, [System.StringComparison]::InvariantCultureIgnoreCase) } 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/cmdlets/common/MethodParameterParameterCompleter.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ScriptClass MethodParameterParameterCompleter { 16 | 17 | function CompleteCommandParameter { 18 | param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) 19 | $graphName = $fakeBoundParameters['GraphName'] 20 | $typeName = $fakeBoundParameters['TypeName'] 21 | $fullyQualified = if ( $fakeBoundParameters['FullyQualifiedTypeName'] ) { 22 | $fakeBoundParameters['FullyQualifiedTypeName'].IsPresent 23 | } else { 24 | $typeName -ne $null -and $typeName.Contains('.') 25 | } 26 | 27 | $methodName = $fakeBoundParameters['MethodName'] 28 | $uriParam = $fakeBoundParameters['Uri'] 29 | 30 | if ( ( ! $typeName -and ! $methodName ) -and ! $uriParam ) { 31 | return $null 32 | } 33 | 34 | $targetContext = $::.ContextHelper |=> GetContextByNameOrDefault $graphName 35 | 36 | if ( $targetContext ) { 37 | $typeManager = $::.TypeManager |=> Get $targetContext 38 | 39 | $targetTypeName = if ( $uriParam ) { 40 | $fullyQualified = $true 41 | $graphNameArgument = if ( $graphName ) { @{GraphName=$graphName} } else { @{} } 42 | $uriInfo = Get-GraphUriInfo $uriParam @graphNameArgument -erroraction stop 43 | if ( $uriInfo ) { 44 | if ( $uriInfo.Class -in 'Action', 'Function' ) { 45 | $methodName = $uriInfo.Name 46 | $uriParentInfo = Get-GraphUriInfo $uriInfo.ParentPath @graphNameArgument -erroraction stop 47 | $uriParentInfo.FullTypeName 48 | } elseif ( $methodName ) { 49 | $uriInfo.FullTypeName 50 | } 51 | } 52 | } elseif ( $typeName ) { 53 | $typeName 54 | } 55 | 56 | $parameterNames = if ( $targetTypeName ) { 57 | $method = $::.TypeMemberFinder |=> FindMembersByTypeName $targetContext $targetTypeName Method $methodName $null $null 58 | if ( $method ) { 59 | $method.Parameters.Name 60 | } 61 | } 62 | 63 | if ( $parameterNames ) { 64 | $parameterNames | where { $_.startswith($wordToComplete, [System.StringComparison]::InvariantCultureIgnoreCase) } 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/cmdlets/common/MethodUriParameterCompleter.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script TypeUriHelper) 16 | . (import-script MethodNameParameterCompleter) 17 | . (import-script MethodParameterParameterCompleter) 18 | 19 | ScriptClass MethodUriParameterCompleter { 20 | $completer = $null 21 | $parameterToComplete = $null 22 | 23 | function __initialize($parameterType) { 24 | $this.completer = if ( $parameterType -eq 'MethodName' ) { 25 | new-so MethodNameParameterCompleter 26 | } elseif ( $parameterType -eq 'ParameterName' ) { 27 | new-so MethodParameterParameterCompleter 28 | } else { 29 | throw [ArgumentException]::new("The specified parameter type '$parameterType' must be one of 'MethodName' or 'ParameterName'") 30 | } 31 | } 32 | 33 | function CompleteCommandParameter { 34 | param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) 35 | 36 | $uriParam = $fakeBoundParameters['Uri'] 37 | $graphNameParam = $fakeBoundParameters['GraphName'] 38 | $methodNameParam = $fakeBoundParameters['MethodName'] 39 | $typeNameParam = $fakeBoundParameters['TypeName'] 40 | $graphObjectParam = $fakeBoundParameters['GraphItem'] 41 | $isFullyQualified = if ( $fakeBoundParameters['FullyQualifiedTypeName'] ) { 42 | $fakeBoundParameters['FullyQualifiedTypeName'].IsPresent 43 | } else { 44 | $false 45 | } 46 | 47 | $targetContext = $::.ContextHelper |=> GetContextByNameOrDefault $graphNameParam 48 | 49 | $typeName = if ( $typeNameParam ) { 50 | $typeNameParam 51 | } else { 52 | $isFullyQualified = $true 53 | $targetUri = if ( $uriParam ) { 54 | $uriParam 55 | } elseif ( $graphObjectParam ) { 56 | if ( $targetContext ) { 57 | $::.TypeUriHelper |=> GetUriFromDecoratedObject $targetContext $graphObjectParam 58 | } 59 | } else { 60 | '.' 61 | } 62 | 63 | if ( $targetUri ) { 64 | $::.TypeUriHelper |=> TypeFromUri $targetUri $targetContext | select -expandproperty FullTypeName 65 | } 66 | } 67 | 68 | if ( ! $typeName ) { 69 | return 70 | } 71 | 72 | $forwardedBoundParams = @{ 73 | TypeName = $typeName 74 | MethodName = $methodNameParam 75 | GraphName = $graphNameParam 76 | Uri = $uriParam 77 | FullyQualifiedTypeName = ([System.Management.Automation.SwitchParameter]::new($isFullyQualified)) 78 | } 79 | 80 | $this.completer |=> CompleteCommandParameter $commandName $parameterName $wordToComplete $commandAst $forwardedBoundParams 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/cmdlets/common/PermissionHelper.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ScriptClass PermissionHelper { 16 | static { 17 | $PermissionDisplayTypeName = '__ScriptClassPermissionDisplayType' 18 | $onlineAttempted = $false 19 | 20 | $typeMap = @{ 21 | Scope='Delegated' 22 | Role='AppOnly' 23 | } 24 | 25 | function __RegisterSegmentDisplayType($typeName) { 26 | remove-typedata -typename $typeName -erroraction ignore 27 | 28 | $coreProperties = @('Type', 'ConsentType', 'Name', 'Description') 29 | 30 | $segmentDisplayTypeArguments = @{ 31 | TypeName = $typeName 32 | MemberType = 'NoteProperty' 33 | MemberName = 'PSTypeName' 34 | Value = $typeName 35 | DefaultDisplayPropertySet = $coreProperties 36 | } 37 | 38 | Update-TypeData -force @segmentDisplayTypeArguments 39 | } 40 | 41 | __RegisterSegmentDisplayType $PermissionDisplayTypeName 42 | 43 | function FindPermission($source, $searchString, $permissionType, $destination, $connection) { 44 | $targetConnection = __GetConnection $connection 45 | 46 | $source | foreach { 47 | if ( ! $searchString -or ( $_.tolower().contains($searchString) ) ) { 48 | $permissionData = $::.ScopeHelper |=> GetPermissionsByName $_ $permissionType $targetConnection $true 49 | $entry = GetPermissionEntry $permissionData 50 | $destination.Add($_, $entry) 51 | } 52 | } 53 | } 54 | 55 | function GetPermissionsByName( [string[]] $scopeNames, $permissionType, $connection, $ignoreNotFound ) { 56 | $targetConnection = __GetConnection $connection 57 | 58 | $::.ScopeHelper |=> GetPermissionsByName $scopeNames $permissionType $targetConnection $ignoreNotFound 59 | } 60 | 61 | function GetKnownPermissionsSorted($connection, $graphAppAuthType) { 62 | $targetConnection = __GetConnection $connection 63 | 64 | $::.ScopeHelper |=> GetKnownPermissionsSorted $targetConnection $graphAppAuthType 65 | } 66 | 67 | function GetPermissionEntry($permissionData) { 68 | [PSCustomObject] @{ 69 | PSTypeName = $this.PermissionDisplayTypeName 70 | Id = $permissionData.Id 71 | PermissionType = $this.typeMap[$permissionData.Type] 72 | ConsentType = $permissionData.consentType 73 | Name = $permissionData.Name 74 | Description = $permissionData.description 75 | } 76 | } 77 | 78 | function ResetOnlineAttempted { 79 | $this.onlineAttempted = $false 80 | } 81 | 82 | function __GetConnection($connection) { 83 | if ( $connection -and ! $this.onlineAttempted ) { 84 | $this.onlineAttempted = $true 85 | # TODO: Implement a method for accessing ScopeHelper state this way and / or a non-static solution 86 | $::.ScopeHelper.retrievedScopesFromGraphService = $false 87 | $connection 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/cmdlets/common/RelationshipDisplayType.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ScriptClass RelationshipDisplayType { 16 | $GraphName = $null 17 | $Relationship = $null 18 | $FromUri = $false 19 | $TargetUri = $null 20 | $TargetId = $null 21 | 22 | function __initialize($graphName, $relationship, $fromUri, $targetUri, $targetId) { 23 | $this.GraphName = $graphName 24 | $this.Relationship = $relationship 25 | $this.FromUri = $fromUri 26 | $this.TargetUri = $targetUri 27 | $this.TargetId = $targetId 28 | } 29 | 30 | static { 31 | function __RegisterDisplayType { 32 | $displayTypeName = $this.ClassName 33 | 34 | $displayProperties = @('Relationship', 'TargetId', 'FromUri', 'TargetUri') 35 | 36 | remove-typedata -typename $displayTypeName -erroraction ignore 37 | 38 | $displayTypeArguments = @{ 39 | TypeName = $displayTypeName 40 | DefaultDisplayPropertySet = $displayProperties 41 | } 42 | 43 | Update-TypeData -force @displayTypeArguments 44 | } 45 | } 46 | } 47 | 48 | $::.RelationshipDisplayType |=> __RegisterDisplayType 49 | -------------------------------------------------------------------------------- /src/cmdlets/common/RequestHelper.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ScriptClass RequestHelper { 16 | static { 17 | function GraphObjectToWriteRequestObject($graphObject, $excludedProperties, [HashTable] $includedProperties) { 18 | $filteredObject = @{} 19 | 20 | $responseObject = if ( $::.SegmentHelper |=> IsGraphSegmentType $graphObject ) { 21 | $graphObject.content 22 | } else { 23 | $graphObject 24 | } 25 | 26 | $properties = $responseObject | get-member -MemberType NoteProperty | where { 27 | # 1. Remove id field since it can never be put / patch / posted 28 | # 2. Also remove any odata properties -- these aren't part of the object 29 | # and were added as metadata to the response by Graph 30 | # 3. Remove any excluded properties -- the object may have read-only properties 31 | # for instance, so the caller will want to exclude them 32 | # 4. Remove any null fields -- its not clear that it is valid to specify a field as 33 | # null on a write request in general, and most APIs seem to fail with it. Workaround 34 | # if null is truly needed is to specify those nulls via the includedProperties parameter 35 | $_.name -ne 'id' -and ! $_.name.startswith('@') -and ($_.name -notin $excludedProperties) -and ( $responseObject.$($_.name) -ne $null ) 36 | } | select -ExpandProperty name 37 | 38 | foreach ( $property in $properties ) { 39 | $filteredObject.Add($property, $responseObject.$property) 40 | } 41 | 42 | if ( $includedProperties ) { 43 | foreach ( $includedPropertyName in $includedProperties.keys ) { 44 | $filteredObject[$includedPropertyName] = $includedProperties[$includedPropertyName] 45 | } 46 | } 47 | 48 | $filteredObject 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/cmdlets/common/TypeHelper.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script ../../typesystem/TypeDefinition) 16 | . (import-script MemberDisplayType) 17 | 18 | ScriptClass TypeHelper { 19 | static { 20 | const DisplayTypeName 'GraphTypeDisplayType' 21 | 22 | $displayProperties = @{ 23 | TypeId = 'TypeId' 24 | Namespace = 'Namespace' 25 | TypeClass = 'Class' 26 | BaseType = 'BaseType' 27 | DefaultUri = 'DefaultUri' 28 | Properties = 'Properties' 29 | RelationshipNames = 'PropertyNames' 30 | Relationships = 'NavigationProperties' 31 | IsComposite = 'IsComposite' 32 | Methods = 'Methods' 33 | NativeSchema = 'NativeSchema' 34 | } 35 | 36 | function __initialize { 37 | __RegisterDisplayType 38 | } 39 | 40 | function ToPublic( $privateObject, $defaultUri, $context ) { 41 | $properties = ($::.MemberDisplayType |=> ToDisplayableMemberList $privateObject.($this.displayProperties.Properties)).Result 42 | $relationships = ($::.MemberDisplayType |=> ToDisplayableMemberList $privateObject.($this.displayProperties.Relationships)).Result 43 | $methods = ($::.MemberDisplayType |=> ToDisplayableMemberList $privateObject.($this.displayProperties.Methods)).Result 44 | 45 | $relationshipNames = if ( $relationships ) { $relationships.Name } 46 | 47 | $result = [PSCustomObject] @{ 48 | TypeId = $privateObject.($this.displayProperties.TypeId) 49 | Namespace = $privateObject.($this.displayProperties.Namespace) 50 | TypeClass = $privateObject.($this.displayProperties.TypeClass) 51 | BaseType = $privateObject.($this.displayProperties.BaseType) 52 | DefaultUri = $defaultUri 53 | RelationshipNames = $relationshipNames 54 | IsComposite = $privateObject.($this.displayProperties.IsComposite) 55 | NativeSchema = $privateObject.($this.displayProperties.NativeSchema) 56 | Relationships = $relationships 57 | Properties = $properties 58 | Methods = $methods 59 | GraphName = $context 60 | } 61 | 62 | $result.psobject.typenames.add($this. DisplayTypeName) 63 | $result 64 | } 65 | 66 | function __RegisterDisplayType { 67 | remove-typedata -typename $this.DisplayTypeName -erroraction ignore 68 | 69 | $coreProperties = @('TypeId', 'TypeClass', 'BaseType', 'DefaultUri', 'Relationships', 'Properties', 'Methods') 70 | 71 | $displayTypeArguments = @{ 72 | TypeName = $this.DisplayTypeName 73 | DefaultDisplayPropertySet = $coreProperties 74 | } 75 | 76 | Update-TypeData -force @displayTypeArguments 77 | 78 | $this.displayProperties.keys | foreach { 79 | $memberArgs = @{ 80 | TypeName = $this.DisplayTypeName 81 | MemberType = 'NoteProperty' 82 | MemberName = $_ 83 | Value = $null 84 | } 85 | 86 | Update-TypeData -force @memberArgs 87 | } 88 | } 89 | } 90 | } 91 | 92 | $::.TypeHelper |=> __initialize 93 | -------------------------------------------------------------------------------- /src/cmdlets/common/TypeMemberFinder.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script ../../typesystem/TypeManager) 16 | . (import-script TypeHelper) 17 | 18 | ScriptClass TypeMemberFinder { 19 | static { 20 | function FindMembersByTypeName($context, $fullyQualifiedTypeName, $memberType, $memberName, $memberFilter, $badTypeMessage, [bool] $literalFilter) { 21 | $typeManager = $::.TypeManager |=> Get $context 22 | 23 | $type = $typeManager |=> FindTypeDefinition Unknown $fullyQualifiedTypeName $true $true 24 | 25 | if ( $type ) { 26 | FindMembersByType $typeManager $type $memberType $memberName $memberFilter 27 | } elseif ( $badTypeMessage ) { 28 | throw $badTypeMessage 29 | } 30 | } 31 | 32 | function FindMembersByType($typeManager, $type, $memberType, $memberName, $memberFilter) { 33 | $fieldMap = [ordered] @{ 34 | Property = 'Property' 35 | Relationship = 'NavigationProperty' 36 | Method = 'Method' 37 | } 38 | 39 | $orderedMemberFields = if ( $memberType ) { 40 | , $fieldMap[$MemberType] 41 | } else { 42 | $fieldMap.values 43 | } 44 | 45 | foreach ( $memberField in $orderedMemberFields ) { 46 | $allMembers = $typeManager |=> GetTypeDefinitionTransitiveProperties $type $memberfield 47 | 48 | $matchingMembers = $allMembers | where { 49 | if ( $memberName ) { 50 | $_.Name -in $memberName 51 | } elseif ( $memberFilter ) { 52 | $targetFilter = if ( $literalFilter ) { 53 | $memberFilter 54 | } else { 55 | "*$($memberFilter)*" 56 | } 57 | $_.Name -like $targetFilter 58 | } else { 59 | $true 60 | } 61 | } 62 | 63 | $matchingMembers | sort-object name | foreach { 64 | new-so MemberDisplayType $_ $type.TypeId 65 | } 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/cmdlets/common/TypeParameterCompleter.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ScriptClass TypeParameterCompleter { 16 | $explicitTypeClasses = $null 17 | $unqualified = $false 18 | 19 | function __initialize( $explicitTypeClass = $null, $unqualified = $false ) { 20 | $this.explicitTypeClasses = $explicitTypeClasses 21 | $this.unqualified = $unqualified 22 | } 23 | 24 | function CompleteCommandParameter { 25 | param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) 26 | $graphName = $fakeBoundParameters['GraphName'] 27 | $fullyQualified = $null 28 | 29 | $typeClass = if ( $this.explicitTypeClasses ) { 30 | $this.explicitTypeClasses 31 | } else { 32 | , $fakeBoundParameters['TypeClass'] 33 | $fullyQualified = if ( $fakeBoundParameters['FullyQualifiedTypeName'] ) { 34 | $fakeBoundParameters['FullyQualifiedTypeName'].IsPresent 35 | } 36 | } 37 | 38 | $targetTypeClasses = if ( ! $typeClass -or $typeClass -eq 'Any' ) { 39 | , 'Unknown' 40 | } else { 41 | $typeClass 42 | } 43 | 44 | $targetWord = if ( $this.unqualified -or $fullyQualified -or ( $typeClass -eq 'Primitive' ) -or $wordToComplete.Contains('.') ) { 45 | $wordToComplete 46 | } else { 47 | 'microsoft.graph.' + $wordToComplete 48 | } 49 | 50 | $targetContext = $::.ContextHelper |=> GetContextByNameOrDefault $graphName 51 | 52 | if ( $targetContext ) { 53 | $typeNames = $::.TypeManager |=> GetSortedTypeNames $targetTypeClasses $targetContext 54 | 55 | $candidates = if ( $this.unqualified ) { 56 | $typeNames | foreach { $_ -split '\.' | select -last 1 } 57 | } else { 58 | $typeNames 59 | } 60 | 61 | $::.ParameterCompleter |=> FindMatchesStartingWith $targetWord $candidates 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/cmdlets/common/TypePropertyParameterCompleter.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2019, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ScriptClass TypePropertyParameterCompleter { 16 | enum PropertyType { 17 | Property 18 | NavigationProperty 19 | } 20 | 21 | $PropertyType = $null 22 | 23 | function __initialize($propertyType = 'Property') { 24 | $this.propertyType = [PropertyType] $propertyType 25 | } 26 | 27 | function CompleteCommandParameter { 28 | param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) 29 | $typeClass = $fakeBoundParameters['TypeClass'] 30 | $graphName = $fakeBoundParameters['GraphName'] 31 | $fullyQualified = if ( $fakeBoundParameters['FullyQualifiedTypeName'] ) { 32 | $fakeBoundParameters.IsPresent 33 | } 34 | $typeName = $fakeBoundParameters['TypeName'] 35 | 36 | if ( ! $typeName ) { 37 | return $null 38 | } 39 | 40 | $targetTypeClass = if ( ! $typeClass -or $typeClass -eq 'Any' ) { 41 | 'Unknown' 42 | } else { 43 | $typeClass 44 | } 45 | 46 | $targetContext = $::.ContextHelper |=> GetContextByNameOrDefault $graphName 47 | 48 | if ( $targetContext ) { 49 | $typeManager = $::.TypeManager |=> Get $targetContext 50 | $isFullyQualified = $fullyQualified -or ( $typeClass -ne 'Primitive' -and $TypeName.Contains('.') ) 51 | 52 | $type = $typeManager |=> FindTypeDefinition $targetTypeClass $typeName $isFullyQualified ($targetTypeClass -ne 'Unknown') 53 | $typeProperties = if ( $type ) { 54 | $typeManager |=> GetTypeDefinitionTransitiveProperties $type $this.propertyType 55 | } 56 | 57 | $typeProperties.name | where { $_.startswith($wordToComplete, [System.StringComparison]::InvariantCultureIgnoreCase) } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/cmdlets/common/TypeSearchResultDisplayType.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ScriptClass TypeSearchResultDisplayType { 16 | $SearchTerm = $null 17 | $Relevance = $null 18 | $TypeClass = $null 19 | $TypeId = $null 20 | $Criteria = $null 21 | $MatchedTerms = $null 22 | $GraphName = $null 23 | $Others = $null 24 | 25 | function __initialize($typeMatch, $graphName) { 26 | $this.SearchTerm = $typeMatch.SearchTerm 27 | $this.Relevance = $typeMatch.Score 28 | $this.TypeClass = $typeMatch.MatchedTypeClass 29 | $this.TypeId = $typeMatch.MatchedTypeName 30 | $this.Criteria = $typeMatch.MatchedTerms.keys 31 | $this.MatchedTerms = $typeMatch.MatchedTerms.values 32 | $this.GraphName = $graphName 33 | } 34 | 35 | static { 36 | function __RegisterDisplayType { 37 | $displayTypeName = $this.ClassName 38 | 39 | $displayProperties = @('TypeClass', 'TypeId', 'Criteria', 'MatchedTerms') 40 | 41 | remove-typedata -typename $displayTypeName -erroraction ignore 42 | 43 | $displayTypeArguments = @{ 44 | TypeName = $displayTypeName 45 | DefaultDisplayPropertySet = $displayProperties 46 | } 47 | 48 | Update-TypeData -force @displayTypeArguments 49 | } 50 | } 51 | } 52 | 53 | 54 | $::.TypeSearchResultDisplayType |=> __RegisterDisplayType 55 | -------------------------------------------------------------------------------- /src/cmdlets/new-graph.tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | Describe 'The New-Graph cmdlet' { 17 | BeforeEach { 18 | Get-Graph | Remove-Graph -warningaction silentlycontinue 3>&1 | out-null 19 | } 20 | 21 | AfterEach { 22 | Get-Graph | Remove-Graph -warningaction silentlycontinue 3>&1 | out-null 23 | } 24 | 25 | Context 'When invoking the New-Graph command' { 26 | $referenceMetadataPath = "$psscriptroot/../../test/assets/betametadata-ns-alias-2020-01-23.xml" 27 | $differenceMetadataPath = "$psscriptroot/../../test/assets/betametadata-ns-alias-multi-namespace-2020-03-25.xml" 28 | 29 | $expectedTypeDiff = @' 30 | [ 31 | { 32 | "InputObject": "microsoft.graph.appscope", 33 | "SideIndicator": "<=" 34 | }, 35 | { 36 | "InputObject": "microsoft.graph.grouppolicyobjectfile", 37 | "SideIndicator": "<=" 38 | }, 39 | { 40 | "InputObject": "microsoft.graph.rbacapplicationmultiple", 41 | "SideIndicator": "<=" 42 | }, 43 | { 44 | "InputObject": "microsoft.graph.unifiedroleassignmentmultiple", 45 | "SideIndicator": "<=" 46 | } 47 | ] 48 | '@ | ConvertFrom-Json 49 | 50 | $incorrectTypeDiff = @' 51 | [ 52 | { 53 | "InputObject": "microsoft.graph.appscope", 54 | "SideIndicator": "<=" 55 | }, 56 | { 57 | "InputObject": "microsoft.graph.grouppolicyobjectfile", 58 | "SideIndicator": "<=" 59 | }, 60 | { 61 | "InputObject": "microsoft.graph.unifiedroleassignmentmultiple", 62 | "SideIndicator": "<=" 63 | } 64 | ] 65 | '@ | ConvertFrom-Json 66 | 67 | It "Should throw an exception if a new graph is created with the same name as an existing graph" { 68 | 69 | $originalGraph = new-graph originalGraph 70 | 71 | { $duplicatedNameGraph = new-graph $originalGraph.Name } | Should Throw 72 | } 73 | 74 | It "Should return the correct difference between two graphs mounted from local metadata" { 75 | 76 | # On PSCore the use of ThreadJob introduces some interesting race conditions that seem to be bugs 77 | # in PowerShell's ThreadJob implementation. Due to those issues, we'll leave this test for 78 | # desktop only for now until we can remove ThreadJob from the implementation. 79 | if ( $PSEdition -eq 'Desktop' ) { 80 | $referenceGraph = new-graph -SchemaUri $ReferenceMetadataPath 81 | $differenceGraph = new-graph -SchemaUri $DifferenceMetadataPath 82 | 83 | $referenceTypes = Get-GraphType -list -GraphName $referenceGraph.Name 84 | $differenceTypes = Get-GraphType -list -Graphname $differenceGraph.Name 85 | 86 | ( $referenceTypes | measure-object ).Count | Should Be 1036 87 | ( $differenceTypes | measure-object ).Count | Should Be 1032 88 | 89 | $actualTypeDiff = Compare-Object $referenceTypes $differenceTypes 90 | 91 | Compare-Object $actualTypeDiff $incorrectTypeDiff | Should Not Be $null 92 | Compare-Object $actualTypeDiff $expectedTypeDiff | Should Be $null 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/common/GraphAccessDeniedException.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2019, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | function __GetGraphAccessDeniedExceptionMessage( [System.Net.WebException] $webException ) { 16 | $response = $webException.Response 17 | $graphUri = $response.ResponseUri.ToString() 18 | $statusCode = if ( $response | gm statuscode -erroraction ignore ) { 19 | $response.statuscode 20 | } 21 | 22 | "Graph endpoint returned http status '$statusCode' accessing '$graphUri'. Retry after re-authenticating via the 'Connect-Graph' cmdlet and requesting appropriate permission scopes for this application. See the following location for documentation on scopes that may apply to this part of the Graph: 'https://developer.microsoft.com/en-us/graph/docs/concepts/permissions_reference'." 23 | } 24 | 25 | class GraphAccessDeniedException : Exception { 26 | GraphAccessDeniedException( [System.Net.WebException] $originalException ) : base((__GetGraphAccessDeniedExceptionMessage $originalException), $originalException) { } 27 | } 28 | -------------------------------------------------------------------------------- /src/common/PreferenceHelper.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | $__GraphMetadataPreferenceValues = @( 16 | 'Ignore', 17 | 'Wait', 18 | 'SilentlyWait' 19 | ) 20 | 21 | $GraphMetadataPreference = 'Wait' 22 | 23 | function __Preference__ShowNotReadyMetadataWarning { 24 | if ( $GraphMetadataPreference -ne 'SilentlyWait' ) { 25 | Write-Warning "Waiting for metadata processing, press CTRL-C to abort" 26 | } 27 | } 28 | 29 | function __Preference__MustWaitForMetadata { 30 | $GraphMetadataPreference -eq 'Wait' -or $GraphMetadataPreference -eq 'SilentlyWait' 31 | } 32 | 33 | $__GraphAutoPromptPreferenceValues = @( 34 | 'Auto', 35 | 'Enable', 36 | 'Disable' 37 | ) 38 | 39 | $GraphAutoPromptPreference = $null 40 | 41 | function __AutoConfigurePrompt($context) { 42 | $originalPrompt = try { 43 | $script:__GraphOriginalPrompt 44 | } catch { 45 | } 46 | 47 | $currentSetting = if ( $GraphAutoPromptPreference ) { 48 | $GraphAutoPromptPreference 49 | } elseif ( $__GraphPromptBehaviorSetting ) { 50 | $__GraphPromptBehaviorSetting 51 | } else { 52 | 'Auto' 53 | } 54 | 55 | if ( $currentSetting -eq 'Auto' ) { 56 | if ( ( $context.connection |=> IsConnected ) -or ! ( $context.location |=> IsRoot ) ) { 57 | if ( ! $originalPrompt ) { 58 | __ConfigurePrompt Enable 59 | } 60 | } else { 61 | if ( $originalPrompt ) { 62 | __ConfigurePrompt Disable 63 | } 64 | } 65 | } elseif ( $currentSetting -eq 'Enable' ) { 66 | __ConfigurePrompt Enable 67 | } elseif ( $currentSetting -eq 'Disable' ) { 68 | __ConfigurePrompt Disable 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /src/formats.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script common/CustomFormatter) 16 | . (import-script cmdlets/common/MetaGraphFormatter) 17 | -------------------------------------------------------------------------------- /src/graph.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | set-strictmode -version 5 16 | 17 | . (import-script metadata/metadata) 18 | . (import-script typesystem/typesystem) 19 | . (import-script cmdlets) 20 | . (import-script settings) 21 | . (import-script aliases) 22 | . (import-script formats) 23 | -------------------------------------------------------------------------------- /src/metadata/Entity.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ScriptClass Entity { 16 | $namespace = $null 17 | $name = $null 18 | $type = $null 19 | $typeData = $null 20 | $navigations = $null 21 | 22 | function __initialize($schema, $namespace) { 23 | $this.namespace = $namespace 24 | 25 | $schemaElement = $schema | select -first 1 26 | $this.type = $schemaElement | select -expandproperty localname 27 | $this.name = $schemaElement | select -first 1 | select -expandproperty name 28 | $this.typeData = $this.GetEntityTypeData($schema) 29 | $this.navigations = if ( ($schema | gm navigationproperty) -ne $null ) { 30 | $schema.navigationproperty | foreach { 31 | new-so Entity $_ $namespace 32 | } 33 | } 34 | } 35 | 36 | function GetEntityId { 37 | '{0}:{1}:{2}' -f $this.type, $this.namespace, $this.name 38 | } 39 | 40 | function GetEntityTypeData($schema) { 41 | $typeData = switch ($this.type) { 42 | 'NavigationProperty' { 43 | $::.GraphUtilities.ParseTypeName($schema.Type) 44 | } 45 | 'NavigationPropertyBinding' { 46 | $::.GraphUtilities.ParseTypeName($schema.parameter.bindingParameter.type) 47 | } 48 | 'Function' { 49 | $::.GraphUtilities.ParseTypeName($schema.ReturnType) 50 | } 51 | } 52 | 53 | if ( ! $typeData ) { 54 | $isCollection = $false 55 | $baseTypeName = $null 56 | $typeName = switch ($this.type) { 57 | 'Singleton' { 58 | $schema.type 59 | } 60 | 'EntityType' { 61 | $baseTypeName = if ( $schema | gm basetype -erroraction ignore ) { 62 | $schema.basetype 63 | } 64 | $::.Entity.QualifyName($this.namespace, $schema.name) 65 | } 66 | 'EntitySet' { 67 | $isCollection = $true 68 | $schema.entitytype 69 | } 70 | 'Action' { 71 | if ( $schema | gm ReturnType ) { 72 | write-verbose "'$($this.name)' is an Action that should have no return type but it is specified to have a return type of $($schema.returntype.type)" 73 | $schema.returntype.type 74 | } 75 | } 76 | '__Scalar' { 77 | $schema.name 78 | } 79 | '__Root' { 80 | $schema.name 81 | } 82 | default { 83 | throw "Unknown entity type $($this.type) for entity name $($this.name)" 84 | } 85 | } 86 | $typeData = [PSCustomObject]@{TypeName=$typeName;IsCollection=$isCollection;BaseTypeName=$baseTypeName} 87 | } 88 | 89 | $typeData 90 | } 91 | 92 | static { 93 | function QualifyName($namespace, $name) { 94 | "{0}.{1}" -f $namespace, $name 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/metadata/EntityEdge.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script Entity) 16 | 17 | ScriptClass EntityEdge { 18 | $sink = $null 19 | $source = $null 20 | $transition = $null 21 | $oneToMany = $null 22 | $name = $null 23 | 24 | function __initialize($source, $sink, $transition) { 25 | if ( $source.IsNull() ) { 26 | throw "An edge with a null source is not valid" 27 | } 28 | 29 | if ( $sink.IsRoot() ) { 30 | throw "'Root' vertex may not be a sink / destination of any edge" 31 | } 32 | 33 | $this.sink = $sink 34 | $this.source = $source 35 | $this.transition = $transition 36 | 37 | $typeData = $transition.typeData 38 | $this.oneToMany = $typeData.isCollection 39 | 40 | $this.name = if ( $transition.type -eq 'NavigationPropertyBinding' ) { 41 | $transition.Path 42 | } elseif ( $transition.type -eq 'NavigationProperty' -or $transition.type -eq 'Action' -or $transition.type -eq 'Function' ) { 43 | $transition.name 44 | } else { 45 | throw "Unknown entity element $($transition.localname)" 46 | } 47 | 48 | $this.scriptclass.count = $this.scriptclass.count + 1 49 | } 50 | 51 | function GetResultTypeData { 52 | $this.sink.GetResultTypeData() 53 | } 54 | 55 | function GetEntity { 56 | $this.transition 57 | } 58 | 59 | static { 60 | $count = 0 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/metadata/EntityVertex.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script Entity) 16 | 17 | ScriptClass EntityVertex { 18 | $id = $null 19 | $name = $null 20 | $entity = $null 21 | $type = $null 22 | $outgoingEdges = $null 23 | $typeName = $null 24 | $baseTypeName = $null 25 | $flags = 0 26 | 27 | function __initialize($entity) { 28 | $this.entity = $entity 29 | 30 | if ( $entity ) { 31 | $this.outgoingEdges = @{} 32 | $this.type = $entity.type 33 | $this.name = $entity.Name 34 | $this.id = $entity.GetEntityId() 35 | $this.typeName = $entity.typedata.TypeName 36 | $this.baseTypeName = $entity.typedata.baseTypeName 37 | } else { 38 | $this.type = 'Null' 39 | $this.name = 'Null' 40 | } 41 | 42 | $this.scriptclass.count = $this.scriptclass.count + 1 43 | } 44 | 45 | function IsNull { 46 | $this.type -eq 'Null' 47 | } 48 | 49 | function IsRoot { 50 | $this.type -eq '__Root' 51 | } 52 | 53 | function SetFlags([int] $flags) { 54 | $this.flags = $this.flags -bOR $flags 55 | } 56 | 57 | function TestFlags([int] $flags) { 58 | $this.flags -bAND $flags 59 | } 60 | 61 | function AddEdge($edge) { 62 | $this.outgoingEdges.Add($edge.name, $edge) 63 | } 64 | 65 | function EdgeExists($entityName) { 66 | $this.outgoingEdges.ContainsKey($entityName) 67 | } 68 | 69 | function GetResultTypeData { 70 | if ( $this.entity ) { 71 | $this.entity.typeData 72 | } else { 73 | @{TypeName='Null';IsCollection=$false} 74 | } 75 | } 76 | 77 | function GetEntity { 78 | $this.entity 79 | } 80 | 81 | static { 82 | $count = 0 83 | $NullVertex = $null 84 | $ScalarVertex = $null 85 | $RootVertex = $null 86 | } 87 | } 88 | 89 | $::.EntityVertex.NullVertex = new-so EntityVertex $null 90 | $::.EntityVertex.ScalarVertex = new-so EntityVertex (new-so Entity ([PSCustomObject] @{Name='scalar';LocalName='__Scalar'})) 91 | $::.EntityVertex.RootVertex = new-so EntityVertex (new-so Entity ([PSCustomObject] @{Name='root';LocalName='__Root'})) 92 | -------------------------------------------------------------------------------- /src/metadata/QualifiedSchema.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ScriptClass QualifiedSchema { 16 | $SchemaClass = $null 17 | $Namespace = $null 18 | $QualifiedName = $null 19 | $Schema = $null 20 | 21 | function __initialize($schemaClass, $namespace, $qualifiedOrUnqualifiedName, $schema) { 22 | if ( ! $namespace -or ! $qualifiedOrUnqualifiedName -or ! $schema ) { 23 | throw "Invalid qualified schema" 24 | } 25 | 26 | if ( $schemaClass -notin 'EntityType', 'EntitySet', 'ComplexType', 'Function', 'Action', 'Singleton', 'EnumType' ) { 27 | throw "Unknown schema class '$schemaClass'" 28 | } 29 | 30 | $this.SchemaClass = $schemaClass 31 | $this.Namespace = $namespace 32 | $this.QualifiedName = $namespace, ($qualifiedOrUnqualifiedName -split '\.' | select -last 1) -join '.' 33 | $this.Schema = $schema 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/metadata/UriCache.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2019, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script GraphSegment) 16 | 17 | ScriptClass UriCache { 18 | $uriTable = strict-val ([ordered] @{}).gettype() $null 19 | $maxEntries = 3 20 | 21 | function __initialize($maxEntries = $null) { 22 | $this.maxEntries = if ( $maxEntries ) { $maxEntries } else { 100 } 23 | $this.uriTable = [ordered] @{} 24 | } 25 | 26 | function Clear { 27 | $this.uriTable.Clear() 28 | } 29 | 30 | function AddUriForSegments($segments, $cacheEntities = $false, $parentSegment = $null) { 31 | $segments | foreach { 32 | 33 | $cacheable = ($_ -ne $null) -and (! $_.isDynamic -or $cacheEntities) 34 | 35 | if ( $cacheable ) { 36 | $segmentUri = $_.graphUri 37 | 38 | if ( ! $this.uriTable[$segmentUri] ) { 39 | write-verbose "Adding uri '$segmentUri' to uri cache" 40 | if ( $this.uriTable.count -ge $this.maxEntries ) { 41 | $removed = $this.uriTable.Remove( ($this.uriTable.keys | select -first 1) ) 42 | write-verbose "Removing uri '$removed' from cache due to maximum entries reached" 43 | } 44 | $this.UriTable.Add($segmentUri, @{Segment=$_;Children=$null}) 45 | } 46 | } 47 | } 48 | 49 | if ( $parentSegment -and ( ! $parentSegment.isDynamic -or $cacheEntities ) ) { 50 | $parentEntry = $this.uriTable[$parentSegment.graphUri] 51 | if ( $parentEntry -and $parentEntry.Children -eq $null ) { 52 | $parentEntry.Children = $segments 53 | } 54 | } 55 | } 56 | 57 | function GetSegmentFromParent($parentSegment, $childSegmentName) { 58 | $targetUri = if ( $parentSegment ) { 59 | $::.GraphUtilities.JoinFragmentUri($parentSegment.graphUri, $childSegmentName) 60 | } else { 61 | '/' + $childSegmentName 62 | } 63 | 64 | $result = GetSegmentFromUri $targetUri 65 | if ( ! $result ) { 66 | write-verbose "Uri '$targetUri' not found in uri cache" 67 | } 68 | $result 69 | } 70 | 71 | function GetSegmentFromUri($uri) { 72 | $result = __GetSegmentDataFromUri($uri) 73 | if ( $result ) { 74 | $result.Segment 75 | } 76 | } 77 | 78 | function GetChildSegmentsFromUri($uri) { 79 | $result = __GetSegmentDataFromUri($uri) 80 | if ( $result ) { 81 | $result.Children 82 | } 83 | } 84 | 85 | function __GetSegmentDataFromUri($uri) { 86 | $result = $this.uriTable[$uri] 87 | if ( $result ) { 88 | $this.uriTable.Remove($result.Segment.graphUri) | out-null 89 | $this.uriTable.Add($result.Segment.GraphUri, $result) 90 | $result 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/metadata/metadata.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script Entity) 16 | . (import-script EntityVertex) 17 | . (import-script EntityEdge) 18 | . (import-script GraphCache) 19 | . (import-script GraphDataModel) 20 | . (import-script EntityGraph) 21 | . (import-script GraphBuilder) 22 | . (import-script GraphSegment) 23 | . (import-script UriCache) 24 | . (import-script SegmentParser) 25 | -------------------------------------------------------------------------------- /src/settings.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script client/GraphLocalSettings) 16 | 17 | $::.LocalProfile |=> ReloadProfileSettings 18 | -------------------------------------------------------------------------------- /src/typesystem/MethodInfo.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ScriptClass MethodInfo { 16 | $Name = $null 17 | $MethodType = $null 18 | $Parameters = $null 19 | $ReturnTypeInfo = $null 20 | $DefiningTypeId = $null 21 | 22 | function __initialize($graph, $methodBindingSchema, $methodType, $definingTypeId) { 23 | if ( $methodType -eq 'Action' ) { 24 | $this.MethodType = 'Action' 25 | } elseif ( $methodType -eq 'Function' ) { 26 | $this.MethodType = 'Function' 27 | } else { 28 | throw [ArgumentException]::new("The specified method type '$methodType' is not valid, it must be one of 'Action', 'Function'.") 29 | } 30 | 31 | $this.Name = $methodBindingSchema.Name 32 | $this.DefiningTypeId = $definingTypeId 33 | 34 | $unaliasedReturnType = $null 35 | $typeInfo = $null 36 | 37 | if ( ( $methodBindingSchema | gm ReturnType -erroraction ignore ) -and 38 | ( $methodBindingSchema.ReturnType | gm Type -erroraction ignore ) ) { 39 | $typeInfo = $::.TypeSchema.GetNormalizedPropertyTypeInfo($null, $methodBindingSchema.ReturnType.Type) 40 | $unaliasedReturnType = $graph.UnaliasQualifiedName($typeInfo.TypeFullName) 41 | 42 | $this.ReturnTypeInfo = [PSCustomObject] @{ 43 | TypeId = $unaliasedReturnType 44 | IsCollection = $typeInfo.IsCollection 45 | } 46 | } 47 | 48 | # Relies on the order of parameters -- we need to skip the first parameter 49 | # This is usually -- but not always! -- named 'bindingParameter'. In some cases, 50 | # it's the name of a type. 51 | # TODO: This may mean that if it's not "bindingParameter" it binds differently 52 | # than we are using it here -- we assume 53 | $bindingParameter = $true 54 | $this.Parameters = foreach ( $parameter in $methodBindingSchema.Parameter ) { 55 | if ( $parameter.name -ne 'bindingParameter' -and ! $bindingParameter ) { 56 | $parameterTypeInfo = $::.TypeSchema.GetNormalizedPropertyTypeInfo($null, $parameter.type) 57 | $unaliasedParameterType = $graph.UnaliasQualifiedName($parameterTypeInfo.TypeFullName) 58 | 59 | [PSCustomObject] @{ 60 | Name = $parameter.name 61 | TypeId = $unaliasedParameterType 62 | IsCollection = $parameterTypeInfo.IsCollection 63 | } 64 | } 65 | 66 | $bindingParameter = $false 67 | } 68 | 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/typesystem/TYPES_DESIGN.md: -------------------------------------------------------------------------------- 1 | Graph Type System modeling in AutoGraphPS 2 | ========================================= 3 | 4 | The TypeSystem subsystem of AutoGraphPS enables Graph API schema browsing and the creation of objects of the types defined in the Graph API schema. A key role for this component is to improve usability of scenarios in which users make requests to create and update requests to the Graph and or specify parameters for action and function invocation requests to the API. 5 | 6 | #### Terms and concepts 7 | 8 | The basis of the type system design includes the following concepts: 9 | 10 | * *Type schema*: This refers formally to the definition of the Graph API's types expressed through the OData standard Common Schema Definition Language (CSDL). 11 | * *Type class* refers to one of the four types that can be expressed in CSDL: Entity, Complex, Enumeration, and Primitive. 12 | * *Type definition* refers to a canonical representation of types expressed in CSDL and OData in a format specific to AutoGraphPS. 13 | * A *Property definition* is the subset of a *Type definition* that describes properties represented in CSDL. 14 | * A *Type Id* is an identifier unique to each type exposed in the CSDL for a given Graph API. 15 | * The *Type provider* class can satsify queries that return *type definitions* and other type metadata for types of a given *type class*. 16 | * A *prototype* for a given type is an object that satisfies the type's *type definition* and therefore also its *type schema*. 17 | 18 | #### Classes 19 | 20 | Below are some schematic representations of the classes for this subsystem. This section does not literally describe the implementation in this codebase, rather it serves as the initial thinking around the responsibility and state for each class. 21 | 22 | * TypeSchema -- helper class with methods for accessing CSDL schema 23 | * TypeDefinition -- abstraction of types defined in the CSDL 24 | * TypeId 25 | * Namespace 26 | * TypeClass (Entity, Complex, Primitive, Enumeration) 27 | * BaseTypeId 28 | * DefaultValue 29 | * DefaultCollectionValue 30 | * Properties 31 | * NativeSchema 32 | * Methods 33 | * Get 34 | * Property -- used to declare properties in a TypeDefinition 35 | * Name 36 | * TypeId 37 | * IsCollection 38 | * TypeManager -- provides access to graph types and prototype objects across Graph APIs 39 | * Methods 40 | * FindTypeDefinition 41 | * GetTypeDefinition 42 | * GetPrototype 43 | * GraphObjectBuilder -- builds a prototype object given a type definition 44 | * Methods 45 | * ToObject 46 | * ScalarTypeProvider -- returns type definitions for scalar types, i.e. types whose structure cannot be decomposed further, specifically Enumeration and Primitive types. 47 | * Methods 48 | * GetTypeDefinition 49 | * GetSortedTypeNames 50 | * CompositeTypeProvider -- returns type definitions for composite types, i.e. types whose structure is composed of properties of other types 51 | * Methods 52 | * GetTypeDefinition 53 | * GetSortedTypeNames 54 | 55 | -------------------------------------------------------------------------------- /src/typesystem/TypeDefinition.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ScriptClass TypeDefinition { 16 | . {}.module.newboundscriptblock($::.TypeSchema.EnumScript) 17 | 18 | $TypeId = $null 19 | $BaseType = $null 20 | $Name = $null 21 | $Namespace = $null 22 | $Properties = $null 23 | $NavigationProperties = $null 24 | $Class = $null 25 | $IsComposite = $false 26 | $DefaultValue = $null 27 | $DefaultCollectionValue = $null 28 | $NativeSchema = $null 29 | $Methods = $null 30 | 31 | function __initialize($typeId, [GraphTypeClass] $class, $name, $namespace, $baseType, $properties, $defaultValue, $defaultCollectionValue, $isComposite, $nativeSchema, $navigationProperties, $methods) { 32 | if ( $class -eq 'Unknown' ) { 33 | throw [ArgumentException]::new("Error creating definition for type '$typeId': the specified type class 'Unknown' is not valid -- the type must be Enumeration, Complex, Primitive, or Entity") 34 | } 35 | 36 | $this.TypeId = $typeId 37 | $this.Class = $class 38 | $this.BaseType = $baseType 39 | $this.Name = $name 40 | $this.Properties = @() 41 | $this.NavigationProperties = @() 42 | $this.Methods = @() 43 | $this.Namespace = $namespace 44 | $this.IsComposite = $IsComposite 45 | $this.DefaultValue = $defaultValue 46 | $this.DefaultCollectionValue = $defaultCollectionValue 47 | $this.NativeSchema = $nativeSchema 48 | 49 | # Also, we only assign to them if the values are not null or empty -- in either case, 50 | # if we return '@()' in an if statement, it gets converted to $nuill on assignment (!) which is not what we want. So we init these 51 | # members to the desired value of @() and then assign to them only if there's something to assign. 52 | if ( $Properties ) { 53 | # Ensure that these members are *ALWAYS* arrays by overriding some PowerShell semantics with singleton arrays via ',' 54 | $this.Properties = if ( ! $properties -or $properties.GetType().IsArray ) { $properties } else { , @($properties) } 55 | } 56 | if ( $NavigationProperties ) { 57 | # See the singleton override workaround for the same case above for Properties 58 | $this.NavigationProperties = if ( ! $navigationProperties -or $navigationProperties.GetType().IsArray ) { $navigationProperties } else { , @($navigationProperties) } 59 | } 60 | 61 | if ( $Methods ) { 62 | $this.Methods = if ( ! $methods -or $methods.GetType().IsArray ) { $methods } else { , @($methods) } 63 | } 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /src/typesystem/TypeIndexEntry.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script TypeSchema) 16 | 17 | ScriptClass TypeIndexEntry { 18 | $lookupValue = $null 19 | $targets = $null 20 | 21 | function __initialize($lookupValue) { 22 | $this.lookupValue = $lookupValue 23 | $this.targets = @{} 24 | } 25 | 26 | function AddTarget($typeId, $typeClass) { 27 | $this.targets.Add($typeId, $typeClass) 28 | } 29 | 30 | function GetTargetTypeIds { 31 | $this.keys 32 | } 33 | 34 | function GetTargetTypeClass($typeId) { 35 | $typeClass = $this.targets[$typeId] 36 | 37 | if ( ! $typeClass ) { 38 | throw "The typeId '$typeId' is not associated with the lookup value '$($this.lookupValue)'" 39 | } 40 | 41 | $typeClass 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/typesystem/TypeMatch.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ScriptClass TypeMatch { 16 | $Criteria = $null 17 | $SearchTerm = $null 18 | $MatchedTypeName = $null 19 | $MatchedTypeClass = $null 20 | $MatchedTerms = $null 21 | $Score = 0 22 | 23 | static { 24 | $MatchWeight = @{ 25 | Name = 16 26 | Property = 8 27 | NavigationProperty = 4 28 | Method = 2 29 | } 30 | } 31 | 32 | function __initialize($criterionName, $searchTerm, $typeName, $matchedTypeClass, [string[]] $matchedTerms) { 33 | $this.Criteria = [ordered] @{} 34 | $this.SearchTerm = $searchTerm 35 | $this.MatchedTypeName = $typeName 36 | $this.MatchedTypeClass = $matchedTypeClass 37 | $this.MatchedTerms = @{$criterionName=$matchedTerms} 38 | 39 | $isExactMatch = $searchTerm -in $matchedTerms 40 | 41 | AddCriterion $criterionName $isExactMatch 42 | } 43 | 44 | function AddCriterion([string] $criterionName, $isExactMatch) { 45 | $this.Criteria.Add($criterionName, [PSCustomObject] @{Name=$criterionName;IsExactMatch=$isExactMatch}) 46 | __UpdateScore 47 | } 48 | 49 | function SetExactMatch($criterionKey) { 50 | if ( $this.Criteria[$criterionKey] ) { 51 | $this.Criteria[$criterionKey].IsExactMatch = $true 52 | } 53 | 54 | __UpdateScore 55 | } 56 | 57 | function Merge($other) { 58 | if ( $this.MatchedTypeName -ne $other.MatchedTypeName ) { 59 | throw "TypeMatch for type '$($other.MatchedTypeName)' may not be merged with source TypeMatch '$($this.MatchedTypeName)'" 60 | } 61 | 62 | foreach ( $criterionKey in $other.Criteria.Keys ) { 63 | $existingCriteria = $this.Criteria[$criterionKey] 64 | if ( ! $existingCriteria ) { 65 | $this.Criteria.Add($criterionKey, [PSCustomObject] @{Name=$criterionKey; IsExactMatch = $false}) 66 | } 67 | } 68 | 69 | foreach ( $matchedCriterion in $other.matchedTerms.Keys ) { 70 | $otherMatch = $other.matchedTerms[$matchedCriterion] 71 | 72 | if ( ! $this.matchedTerms[$matchedCriterion] ) { 73 | $this.matchedTerms[$matchedCriterion] = $otherMatch 74 | } 75 | } 76 | 77 | __UpdateScore 78 | } 79 | 80 | function __UpdateScore { 81 | $score = $this.Criteria.Count 82 | 83 | foreach ( $criterionName in $this.Criteria.Keys ) { 84 | $score += __GetWeight $this.Criteria[$criterionName] 85 | } 86 | 87 | $this.score = $score 88 | } 89 | 90 | function __GetWeight($criterion) { 91 | $knownWeight = $this.scriptclass.MatchWeight[$criterion.Name] 92 | 93 | $normalizedWeight = if ( $knownWeight ) { 94 | $knownWeight 95 | } else { 96 | 1 97 | } 98 | 99 | if ( $criterion.IsExactMatch ) { 100 | $normalizedWeight * 2 101 | } else { 102 | $normalizedWeight 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/typesystem/TypeMember.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ScriptClass TypeMember { 16 | $Name = $null 17 | $TypeId = $null 18 | $IsCollection = $false 19 | $MemberType = $null 20 | $MemberData = $null 21 | $DefiningTypeId = $null 22 | 23 | function __initialize($name, $typeId, [bool] $isCollection, $memberType, $memberData, $definingTypeId) { 24 | $this.Name = $name 25 | $this.MemberData = $memberData 26 | $this.TypeId = $typeId 27 | $this.IsCollection = $isCollection 28 | $this.DefiningTypeId = $definingTypeId 29 | $this.MemberType = if ( $memberType -eq $null -or $MemberType -in 'Property' ) { 30 | 'Property' 31 | } elseif ( $memberType -eq 'Method' ) { 32 | if ( $memberData.ReturnTypeInfo ) { 33 | $this.TypeId = $memberData.ReturnTypeInfo.TypeId 34 | $this.IsCollection = $memberData.ReturnTypeInfo.IsCollection 35 | } 36 | 'Method' 37 | } elseif ( $memberType -eq 'NavigationProperty' ) { 38 | 'Relationship' 39 | } elseif ( $memberType -ne 'Enumeration' ) { 40 | throw [ArgumentException]::new("Invalid member type '$memberType' specified: member type must be one of 'Property' or 'NavigationProperty'") 41 | } 42 | } 43 | 44 | static { 45 | function __RegisterDisplayType { 46 | $displayTypeName = $this.ClassName 47 | 48 | $displayProperties = @('Name', 'MemberType', 'TypeId', 'IsCollection') 49 | 50 | remove-typedata -typename $displayTypeName -erroraction ignore 51 | 52 | $displayTypeArguments = @{ 53 | TypeName = $displayTypeName 54 | DefaultDisplayPropertySet = $displayProperties 55 | } 56 | 57 | Update-TypeData -force @displayTypeArguments 58 | } 59 | } 60 | } 61 | 62 | 63 | $::.TypeMember |=> __RegisterDisplayType 64 | -------------------------------------------------------------------------------- /src/typesystem/TypeProvider.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script TypeDefinition) 16 | 17 | ScriptClass TypeProvider { 18 | $graph = $null 19 | $derived = $null 20 | 21 | function __initialize($derived, $graph) { 22 | $this.derived = $derived 23 | $this.graph = $graph 24 | } 25 | 26 | function GetTypeDefinition($typeClass, $typeId, $ignoreIfNotFound) { 27 | if ( ! $this.derived ) { 28 | throw "Abstract class '$($this.scriptclass.classname)' may not be directly instantiated" 29 | } 30 | $this.derived |=> GetTypeDefinition $typeClass $typeId $ignoreIfNotFound 31 | } 32 | 33 | function GetSortedTypeNames($typeClass) { 34 | if ( ! $this.derived ) { 35 | throw "Abstract class '$($this.scriptclass.classname)' may not be directly instantiated" 36 | } 37 | $this.derived |=> GetSortedTypeNames $typeClass 38 | } 39 | 40 | function UpdateTypeIndexes($indexes, $typeClasses) { 41 | if ( ! $this.derived ) { 42 | throw "Abstract class '$($this.scriptclass.classname)' may not be directly instantiated" 43 | } 44 | $this.derived |=> UpdateTypeIndexes $indexFields 45 | } 46 | 47 | static { 48 | . {}.Module.NewBoundScriptBlock($::.TypeSchema.EnumScript) 49 | 50 | const REQUIRED_ENUM_AS_PRIMITIVE_TYPE ( 51 | [PSCustomObject] @{ 52 | TypeClass = . { [GraphTypeClass]::Primitive } 53 | TypeId = 'Edm.String' 54 | } 55 | ) 56 | 57 | $providersByScriptClass = @{} 58 | 59 | $providerModels = @{ 60 | Unknown = 'CompositeTypeProvider' 61 | Primitive = 'ScalarTypeProvider' 62 | Enumeration = 'ScalarTypeProvider' 63 | Complex = 'CompositeTypeProvider' 64 | Entity = 'CompositeTypeProvider' 65 | } 66 | 67 | function GetDefaultNamespace($typeClass = 'Unknown', $graph) { 68 | $providerModel = GetProviderForClass $typeClass 69 | $providerModel |::> GetDefaultNamespace $typeClass $graph 70 | } 71 | 72 | function GetRequiredTypeInfo { 73 | @($this.REQUIRED_ENUM_AS_PRIMITIVE_TYPE) 74 | } 75 | 76 | function GetProviderForClass($typeClass = 'Unknown') { 77 | $this.providerModels[$typeClass.tostring()] 78 | } 79 | 80 | function ValidateTypeClass($derivedClass, $typeClass = 'Unknown') { 81 | $supportedClasses = $derivedClass |=> GetSupportedTypeClasses 82 | 83 | if ( ! ( $typeClass -in $supportedClasses ) ) { 84 | throw "The '$($this.scriptclass.classname)' type provider does not support type class '$typeClass'" 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/typesystem/TypeSchema.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ScriptClass TypeSchema { 16 | static { 17 | 18 | $EnumScript = { 19 | enum GraphTypeClass { 20 | Unknown 21 | Primitive 22 | Enumeration 23 | Complex 24 | Entity 25 | } 26 | } 27 | 28 | function GetTypeNameInfo($namespace, $qualifiedTypeName) { 29 | $unqualifiedTypeName = if ( $namespace ) { 30 | $qualifiedTypeName.substring($namespace.length + 1, $qualifiedTypeName.length - $namespace.length - 1) 31 | } else { 32 | $qualifiedTypeName 33 | } 34 | 35 | [PSCustomObject] @{ 36 | Namespace = $namespace 37 | Name = $unqualifiedTypeName 38 | } 39 | } 40 | 41 | function GetQualifiedTypeName($namespace, $unqualifiedName) { 42 | if ( $namespace ) { 43 | $namespace, $unqualifiedName -join '.' 44 | } else { 45 | $unqualifiedName 46 | } 47 | } 48 | 49 | function GetNormalizedPropertyTypeInfo($namespace, $typeSpec) { 50 | $typeData = $::.GraphUtilities.ParseTypeName($typeSpec) 51 | 52 | [PSCustomObject] @{ 53 | TypeFullName = $typeData.TypeName 54 | IsCollection = $typeData.isCollection 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/typesystem/TypeSearcher.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script TypeMatch) 16 | . (import-script TypeTable) 17 | 18 | ScriptClass TypeSearcher { 19 | $typeTable = $null 20 | 21 | function __initialize($typeProviders) { 22 | $this.typeTable = new-so TypeTable $typeProviders 23 | } 24 | 25 | function Search($searchFields) { 26 | $typeMatches = @{} 27 | foreach ( $field in $searchFields ) { 28 | $fieldMatches = $this.typeTable |=> FindTypeInfoByField $field.Name $field.SearchTerm $field.TypeClasses $field.LookupClass 29 | 30 | foreach ( $match in $fieldMatches ) { 31 | $existingMatch = $typeMatches[$match.MatchedTypeName] 32 | if ( $existingMatch ) { 33 | $existingMatch |=> Merge $match 34 | } else { 35 | $typeMatches.Add($match.MatchedTypeName, $match) 36 | } 37 | } 38 | } 39 | 40 | $typeMatches.Values 41 | } 42 | 43 | function GetTypeStatistics([string[]] $indexClasses) { 44 | $this.TypeTable |=> GetStatistics $indexClasses 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/typesystem/TypeTable.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2021, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script TypeIndex) 16 | 17 | ScriptClass TypeTable { 18 | # This class allows for fast lookup of types based on specific 19 | # fields in the metadata of the types, including the names, properties, 20 | # and methods of the types. The quick lookup us implemented by 21 | # indexing these fields. 22 | 23 | $types = $null 24 | $typeProviders = $null 25 | $indexes = $null 26 | 27 | function __initialize([HashTable] $typeProviders) { 28 | $this.types = [System.Collections.Generic.SortedDictionary[string, object]]::new(([System.StringComparer]::OrdinalIgnoreCase)) 29 | $this.indexes = @{} 30 | $this.typeProviders = $typeProviders 31 | 32 | # Create empty indexes for each desired field 33 | 'Name', 'Property', 'NavigationProperty', 'Method' | foreach { 34 | $index = new-so TypeIndex $_ 35 | $this.indexes.Add($_, @{Index=$index;Initialized=@{}}) 36 | } 37 | } 38 | 39 | function FindTypeInfoByField([TypeIndexClass] $indexClass, $lookupValue, [string[]] $classes, [TypeIndexLookupClass] $lookupClass = 'Exact') { 40 | $searchMethod = switch ( $lookupClass ) { 41 | 'Exact' { 'Find' } 42 | 'StartsWith' { 'FindStartsWith' } 43 | 'Contains' { 'FindContains' } 44 | default { 45 | throw "Unkonwn lookup class '$lookupClass'" 46 | } 47 | } 48 | 49 | $indexInfo = $this.indexes[$indexClass.tostring()] 50 | __InitializeIndexForTypeClasses $indexInfo $classes 51 | 52 | $indexInfo.Index |=> $searchMethod $lookupValue $classes 53 | } 54 | 55 | function GetStatistics([TypeIndexClass[]] $indexClasses) { 56 | foreach ( $indexClass in $indexClasses ) { 57 | $indexInfo = $this.indexes[$indexClass.tostring()] 58 | __InitializeIndexForTypeClasses $indexInfo 'Entity', 'Complex', 'Enumeration' 59 | } 60 | 61 | # Treat the name index as having one entry per type 62 | $typeCountStatistics = $this.indexes['Name'].Index |=> GetStatistics 63 | $propertyCountStatistics = $this.indexes['Property'].Index |=> GetStatistics 64 | $navigationPropertyCountStatistics = $this.indexes['NavigationProperty'].Index |=> GetStatistics 65 | $methodCountStatistics = $this.indexes['Method'].Index |=> GetStatistics 66 | 67 | $methodCount = if ( $this.indexes['Method'].Initialized['Entity'] ) { 68 | $methodCountStatistics.EntityCount # Methods currently only exist on entities 69 | } 70 | 71 | [PSCustomObject] @{ 72 | EntityCount = $typeCountStatistics.EntityCount 73 | EntityPropertyCount = $propertyCountStatistics.EntityCount 74 | EntityNavigationPropertyCount = $navigationPropertyCountStatistics.EntityCount 75 | ComplexCount = $typeCountStatistics.ComplexCount 76 | ComplexPropertyCount = $propertyCountStatistics.ComplexCount 77 | EnumerationCount = $typeCountStatistics.EnumerationCount 78 | EnumerationValueCount = $propertyCountStatistics.EnumerationCount 79 | MethodCount = $methodCount 80 | } 81 | } 82 | 83 | function __InitializeIndexForTypeClasses($indexInfo, [string[]] $typeClasses) { 84 | foreach ( $typeClass in $typeClasses ) { 85 | if ( ! $indexInfo.Initialized[$typeClass] ) { 86 | $typeProvider = $this.typeProviders[$typeClass] 87 | $indexesForTypeClass = $typeProvider |=> UpdateTypeIndexes @($indexInfo.Index) $typeClass 88 | $indexInfo.Initialized[$typeClass] = $true 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/typesystem/typesystem.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | . (import-script TypeMember) 16 | . (import-script TypeSchema) 17 | . (import-script TypeDefinition) 18 | . (import-script TypeProvider) 19 | . (import-script ScalarTypeProvider) 20 | . (import-script CompositeTypeProvider) 21 | . (import-script TypeManager) 22 | . (import-script GraphObjectBuilder) 23 | 24 | -------------------------------------------------------------------------------- /test/CI/Get-CIPipelineCredential.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2022, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [cmdletbinding()] 16 | param( 17 | [string] $pfxBase64Secret 18 | ) 19 | 20 | Set-StrictMode -Version 2 21 | 22 | if ( $pfxBase64Secret ) { 23 | $secretBytes = [System.Convert]::FromBase64String($pfxBase64Secret) 24 | [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($secretBytes) 25 | # $certCollection = [System.Security.Cryptography.X509Certificates.X509Certificate2Collection]::new() 26 | # $certCollection.Import($secretBytes, $null, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable) 27 | } 28 | -------------------------------------------------------------------------------- /test/CI/PesterDirectRunInit.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2019, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | # Some .psd1 modules fail to load on Linux only when loaded for tests 17 | # executed outside of the import-devmodule environment 18 | # during CI runs (no repro on dev workstations :)). This 19 | # mode of execution is itself a workaround for a Linux-only 20 | # hang when the pester tests are run with import-devmodule. 21 | # So until that's fixed, the workaround is to import the psm1 22 | # directly. Once import-devmodule is fixed, this workaround 23 | # shoudl be removed. 24 | 25 | import-module scriptclass 26 | import-module (Get-ModulePSMPath AutoGraphPS-SDK) 27 | import-module (Get-ModulePSMPath AutoGraphPS) 28 | -------------------------------------------------------------------------------- /test/CI/RunIntegrationTests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2022, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [cmdletbinding(positionalbinding=$false)] 16 | param( 17 | [parameter(position=0)] 18 | [string] $TestRoot = 'test', 19 | 20 | [string] $TestAppId, 21 | 22 | [string] $TestAppTenant, 23 | 24 | [string] $CIBase64TestAppCert, 25 | 26 | [HashTable] $TestParamsPassThru 27 | ) 28 | 29 | Set-StrictMode -Version 2 30 | 31 | . "$psscriptroot/../../build/common-build-functions.ps1" 32 | 33 | $baseDirectory = Get-SourceRootDirectory 34 | 35 | $targetRoot = if ( $TestRoot ) { 36 | join-path $baseDirectory $TestRoot 37 | } else { 38 | $baseDirectory 39 | } 40 | 41 | if ( ! ( Test-Path $targetRoot ) ) { 42 | throw "Specified subdirectory '$targetRoot' could not be found under '$baseDirectory' -- the path '$targetRoot' is not valid." 43 | } 44 | 45 | # Use -Force since dot directories like .test are "hidden" on *nix without it. 46 | $targetRootPath = (get-item -Force $targetRoot).FullName 47 | 48 | write-verbose "Preparing to execute integration tests under directory '$targetRootPath'..." 49 | 50 | & $psscriptroot/../../build/Init-DirectTestRun.ps1 51 | 52 | $appCert = if ( $CIBase64TestAppCert ) { 53 | write-verbose "CI pipeline test application credential was specified" 54 | & $psscriptroot/Get-CIPipelineCredential.ps1 $CIBase64TestAppCert 55 | } else { 56 | write-verbose "No CI pipeline credential was specified, local configuration will be used for test app credential" 57 | } 58 | 59 | $testParams = @{} 60 | 61 | if ( $TestAppId ) { 62 | $testParams['TestAppId'] = $TestAppId 63 | } 64 | 65 | if ( $TestAppTenant ) { 66 | $testParams['TestAppTenant'] = $TestAppTenant 67 | } 68 | 69 | if ( $appCert ) { 70 | $testParams['TestAppCertificate'] = $appCert 71 | } 72 | 73 | & $psscriptroot/../Initialize-IntegrationTestEnvironment.ps1 @testParams 74 | 75 | Invoke-Pester -Script $targetRootPath @TestParamsPassThru 76 | 77 | -------------------------------------------------------------------------------- /test/Clean-IntegrationTestState.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2023, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [cmdletbinding(positionalbinding=$false, supportsshouldprocess=$true)] 16 | param( 17 | [parameter(mandatory=$true)] 18 | [Guid] $TestAppId, 19 | 20 | [parameter(mandatory=$true)] 21 | $GraphConnection, 22 | 23 | [string] $TestRunId, 24 | 25 | [switch] $EnableDelete 26 | ) 27 | 28 | Set-StrictMode -Version 2 29 | 30 | $ErrorActionPreference = 'Stop' 31 | 32 | $disallowedOwners = '9825d80c-5aa0-42ef-bf13-61e12116704c', 'ac70e3e2-a821-4d19-839c-b8af4515254b' 33 | $disallowedDeletedAppIds = $disallowedOwners + ( $GraphConnection.identity.app.appid ) 34 | 35 | if ( $TestAppId -in $disallowedOwners ) { 36 | write-error "This script may not be used to delete state owned by the specified TestAppId '$TestAppId'" 37 | } 38 | 39 | # Clean up applications created by the test app 40 | 41 | $servicePrincipal = Get-GraphApplicationServicePrincipal -AppId $TestAppId -Connection $GraphConnection 42 | 43 | $applications = Get-GraphResource /servicePrincipals/$($servicePrincipal.Id) -Expand ownedObjects -Connection $GraphConnection | 44 | select-object -expandproperty ownedObjects | 45 | where { 46 | $_.'@odata.type' -eq '#microsoft.graph.application' 47 | } 48 | 49 | foreach ( $application in $applications ) { 50 | $appUri = "/applications/$($application.Id)" 51 | 52 | write-verbose "Found application with application id $($application.AppId) and object id $($application.id)" 53 | 54 | if ( $application.AppId -in $disallowedDeletedAppIds ) { 55 | write-error "Deletion of the specified object $($application.Id) with app id $($application.AppId) is not supported by this script" 56 | } 57 | 58 | if ( $TestRunId ) { 59 | if ( $application.Tags -contains $TestRunId ) { 60 | write-verbose "A TestRunId '$TestRunId' was specified, but the Tags property of owned application with object id '$($application.Id)' does not contain that TestRunId value" 61 | continue 62 | } 63 | } 64 | 65 | if ( $EnableDelete.IsPresent ) { 66 | Remove-GraphApplication -ObjectId $application.Id -Connection $GraphConnection 67 | write-verbose "Successfully deleted application id $($application.AppId) and object id $($application.Id)" 68 | } else { 69 | write-verbose 'Skipping deletion because the EnableDelete parameter was not specified' 70 | } 71 | 72 | $appUri 73 | } 74 | 75 | 76 | -------------------------------------------------------------------------------- /test/Get-IntegrationTestAppCertificates.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2022, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [cmdletbinding()] 16 | 17 | if ( ! $psscriptroot/IsIntegrationTestRun.ps1 ) { 18 | throw 'Unable to get integration test certificates because this is not an integration test run.' 19 | } 20 | 21 | if ( $__CI_PIPELINE ) { 22 | if ( ! $__CI_PIPELINE_CERT_PATH ) { 23 | throw 'No certificate path for the CI pipeline is defined' 24 | } 25 | 26 | $certData = Get-Content $__CI_PIPELINE_CERT_PATH -ErrorAction Stop 27 | 28 | $ciCredential = $psscriptroot/CI/Get-CIPipelineCredential.ps1 29 | 30 | if ( ! $ciCredential ) { 31 | throw 'The CI pipeline credential is null / empty.' 32 | } 33 | 34 | [PSCustomObject] @{ 35 | AppCert = $ciCredential 36 | } 37 | } else { 38 | if ( ! $__LOCAL_TEST_CERT_PATH ) { 39 | throw "The local development PowerShell session is configured for integration tests with __IntegrationTestRoot variable set to to the non-empty value '$__IntegrationTestRoot' but the __LOCAL_TEST_CERT_PATH variable is not defined. Set this variable to a valid file system or certificate store path and retry the operation, or clear / delete the __IntegrationTestRoot variable to skip the attempt to run integration tests altogether." 40 | } 41 | $localTestCredential = Get-Item $__LOCAL_TEST_CERT_PATH -ErrorAction Stop 42 | 43 | [PSCustomObject] @{ 44 | AppCert = $localTestCredential 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/Get-IntegrationTestEntityName.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2022, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [cmdletbinding()] 16 | param ( 17 | [parameter(parametersetname='entityname', mandatory=$true)] 18 | [string] $Name, 19 | 20 | [parameter(parametersetname='entityname')] 21 | [string] $TestId, 22 | 23 | [parameter(parametersetname='entityname')] 24 | [ValidateNotNullOrEmpty()] 25 | [string] $TestSuite, 26 | 27 | [parameter(parametersetname='entityname')] 28 | [ValidateNotNullOrEmpty()] 29 | [string] $Prefix, 30 | 31 | [parameter(parametersetname='prefixonly')] 32 | [switch] $PrefixOnly 33 | ) 34 | 35 | $targetTestId = if ( $TestId ) { 36 | $TestId 37 | } elseif ( $__IntegrationTest_TestId ) { 38 | $__IntegrationTest_TestId 39 | } else { 40 | 'Global' 41 | } 42 | 43 | $targetTestSuite = if ( $TestSuite ) { 44 | $TestSuite 45 | } elseif ( $__IntegrationTest_TestId ) { 46 | $__IntegrationTest_TestSuite 47 | } else { 48 | 'Default' 49 | } 50 | 51 | $targetPrefix = if ( $Prefix ) { 52 | $Prefix 53 | } elseif ( $__IntegrationTest_Prefix ) { 54 | $__IntegerationTest_Prefix 55 | } else { 56 | '__INT_TEST' 57 | } 58 | 59 | if ( $PrefixOnly.IsPresent ) { 60 | $targetPrefix 61 | } else { 62 | "targetPrefix-$targetTestSuite-$targetTestId-$Name" 63 | } 64 | 65 | 66 | -------------------------------------------------------------------------------- /test/Get-IntegrationTestStatus.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2022, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [cmdletbinding()] 16 | param([string] $TestConfigDirectoryPath) 17 | 18 | Set-StrictMode -Version 2 19 | 20 | $isIntegrationRun = & "$psscriptRoot/IsIntegrationTestRun.ps1" 21 | 22 | $targetTestConfigDirectory = if ( $TestConfigDirectoryPath ) { 23 | $testConfigDirectoryPath 24 | } else { 25 | $configParent = ( Get-Item "$psscriptroot/.." ).FullName 26 | join-path $configParent .testconfig 27 | } 28 | 29 | $testRunIdPathName = join-path $targetTestConfigDirectory 'TestRunId.txt' 30 | $targetTestConfigPath = join-path $targetTestConfigDirectory 'TestConfig.json' 31 | 32 | $testRunId = if ( test-path $testRunIdPathName ) { 33 | Get-Content $testRunIdPathName | out-string 34 | } else { 35 | '__GLOBAL_AUTOGRAPH_INTEGRATION_TEST_RUN' 36 | } 37 | 38 | $testAppId = $null 39 | $testAppTenant = $null 40 | $testAppCertPath = $null 41 | $graphConnection = ( Get-Variable -Scope Global __IntegrationTestGraphConnection -Value -ErrorAction Ignore ) 42 | 43 | if ( test-path $targetTestConfigPath ) { 44 | write-verbose "Reading test config at path '$targetTestConfigPath'" 45 | $config = get-content $targetTestConfigPath | out-string | ConvertFrom-json 46 | $testAppId = $config | select-object -expandproperty TestAppId -ErrorAction Ignore 47 | $testAppTenant = $config | select-object -expandproperty TestAppTenant -ErrorAction Ignore 48 | $testAppCertPath = $config | select-object -expandproperty TestAppCertificatePath -ErrorAction Ignore 49 | 50 | $certArg = if ( $testAppCertPath ) { 51 | @{CertificatePath = $testAppCertPath} 52 | } 53 | 54 | $newConnection = New-GraphConnection -AppId $testAppId -TenantId $testAppTenant @certArg -NoninteractiveAppOnlyAuth 55 | 56 | # Only alter the environment to synchronize it with the file contents -- if it isn't already set. 57 | if ( $graphConnection ) { 58 | write-verbose "Existing connection specified for __IntegrationTestGraphConnection, overriding with file contents for consistency" 59 | Set-Variable -Scope Global __IntegrationTestGraphConnection -Value $graphConnection 60 | } 61 | 62 | $graphConnection = $newConnection 63 | } else { 64 | write-verbose "Skipping read of test config at path '$targetTestConfigPath' because it doesn't exist. Getting info from __IntegrationTestGraphConnection instead." 65 | 66 | if ( $graphConnection ) { 67 | Connect-GraphApi -Connection $graphConnection -NoSetCurrentConnection | out-null 68 | $testAppId = $graphConnection.Identity.App.AppId 69 | $testAppTenant = $graphConnection.Identity.TenantDisplayId 70 | } 71 | } 72 | 73 | [PSCustomObject] @{ 74 | IsIntegrationRun = $isIntegrationRun 75 | ConfigurationPath = $targetTestConfigPath 76 | TestRunId = $testRunId 77 | TestRunIdPathName = $testRunIdPathName 78 | TestAppId = $testAppId 79 | TestAppTenant = $testAppTenant 80 | TestAppCertPath = $testAppCertPath 81 | GraphConnection = $graphConnection 82 | } 83 | -------------------------------------------------------------------------------- /test/Initialize-IntegrationTestEnvironment.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2022, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [cmdletbinding(positionalbinding=$false, defaultparametersetname='configfile')] 16 | param( 17 | [string] $TestRoot, 18 | 19 | [parameter(parametersetname='configfile')] 20 | [string] $TestConfigPath, 21 | 22 | [parameter(parametersetname='explicit', mandatory=$true)] 23 | [string] $TestAppId, 24 | 25 | [parameter(parametersetname='explicit', mandatory=$true)] 26 | [string] $TestAppTenant, 27 | 28 | [parameter(parametersetname='explicit', mandatory=$true)] 29 | [System.Security.Cryptography.X509Certificates.X509Certificate2] $TestAppCertificate, 30 | 31 | $GraphConnectionOverride 32 | ) 33 | 34 | Set-StrictMode -Version 2 35 | 36 | $global:__IntegrationTestInfo = $null 37 | 38 | if ( ! ( Get-Command New-GraphConnection -erroraction ignore ) ) { 39 | throw "The 'New-GraphConnection' command could not be found. Please ensure that the module that contains the 'New-GraphConnection' command has been loaded into the session and retry the operation." 40 | } 41 | 42 | $global:__IntegrationTestRoot = if ( $TestRoot ) { 43 | $TestRoot 44 | } else { 45 | $psscriptroot 46 | } 47 | 48 | $config = & $psscriptroot/Get-IntegrationTestStatus.ps1 49 | 50 | $targetTestAppId = if ( $TestAppId ) { 51 | $TestAppId 52 | } else { 53 | $config.TestAppId 54 | } 55 | 56 | $targetTestAppTenant = if ( $TestAppTenant ) { 57 | $TestAppTenant 58 | } else { 59 | $config.TestAppTenant 60 | } 61 | 62 | $targetConnection = if ( $GraphConnectionOverride ) { 63 | $GraphConnectionOverride 64 | } elseif ( $targetTestAppId -and $targetTestAppTenant ) { 65 | $certArgument = @{} 66 | if ( $TestAppCertificate ) { 67 | $certArgument.Add('Certificate', $TestAppCertificate) 68 | } elseif ( $config.TestAppCertPath ) { 69 | $certArgument.Add('CertificatePath', $config.TestAppCertPath) 70 | } 71 | 72 | New-GraphConnection -AppId $targetTestAppId -TenantId $targetTestAppTenant -NoninteractiveAppOnlyAuth @certArgument 73 | } 74 | 75 | if ( ! $targetConnection ) { 76 | throw "No Graph connection information was specified. Please specify the 'TestAppId', 'TestAppTenant', and if required the 'TestAppCertificatePath' properties in the test configuration file '$($config.ConfigurationPath)' or specify a Graph Connection object created by the 'New-GraphConnection' command to the GraphConnectionOverride parameter of this command and retry the operation" 77 | } 78 | 79 | $global:__IntegrationTestGraphConnection = $TargetConnection 80 | 81 | $global:__IntegrationTestInfo = & $psscriptroot/Get-IntegrationTestStatus.ps1 82 | 83 | -------------------------------------------------------------------------------- /test/IsIntegrationTestRun.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2022, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [cmdletbinding()] 16 | param() 17 | if ( $__IntegrationTestRoot ) { 18 | if ( ! ( test-path $__IntegrationTestRoot ) ) { 19 | throw "The `$__IntegrationTestRoot variable is defined but it is set to the value '$__IntegrationTestroot' which is not a valid path" 20 | } 21 | 22 | $true 23 | } else { 24 | $false 25 | } 26 | -------------------------------------------------------------------------------- /test/Set-IntegrationTestStatus.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2022, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [cmdletbinding()] 16 | param( 17 | [ValidateSet('Enabled', 'Disabled')] 18 | $Status = 'Enabled', 19 | 20 | [Switch] $UpdateTestRunId, 21 | 22 | $testScriptRoot = $null 23 | ) 24 | 25 | if ( $UpdateTestRunId.IsPresent ) { 26 | $testStatus = & $psscriptroot/Get-IntegrationTestStatus.ps1 27 | $testId = new-guid 28 | Set-Content -Path $testStatus.TestRunIdPathName -Value $testId.Guid.ToString() 29 | $global:__IntegrationTest_TestId = $testId.Guid.ToString() 30 | } elseif ( $Status -eq 'Enabled' ) { 31 | $global:__IntegrationTestRoot = if ( $testScriptRoot ) { 32 | if ( ! ( test-path $testScriptRoot ) ) { 33 | throw "The specified path '$testScriptRoot' is not valid or does not exist" 34 | } 35 | $testScriptRoot 36 | } else { 37 | $psscriptRoot 38 | } 39 | } else { 40 | $testVariable = Get-Variable __IntegrationTestRoot -ErrorAction Ignore 41 | 42 | if ( $testVariable ) { 43 | $testVariable | Set-Variable -Value $null 44 | } 45 | 46 | if ( $__IntegrationTestRoot ) { 47 | throw 'Failed to disable integration testing status by setting $__IntegrationTestRoot variable to $null -- the variable may be defined at multiple scopes. Remove the variable at all scopes or set it to null in all scopes and retry the command.' 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /test/assets/GetGraphUriMe.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adamedx/autographps/5e154c1fb03d3074d59f429639936f50a1b76dd3/test/assets/GetGraphUriMe.json -------------------------------------------------------------------------------- /test/assets/NewGraphBetaObjectApplication.json: -------------------------------------------------------------------------------- 1 | { 2 | "deletedDateTime": "\/Date(1577865600000)\/", 3 | "api": null, 4 | "appId": "", 5 | "displayName": "", 6 | "tags": [ 7 | "" 8 | ], 9 | "web": null, 10 | "isFallbackPublicClient": false, 11 | "createdDateTime": "\/Date(1577865600000)\/", 12 | "publicClient": null, 13 | "tokenEncryptionKeyId": "00000000-0000-0000-0000-000000000000", 14 | "info": null, 15 | "logo": null, 16 | "isDeviceOnlyAuthSupported": false, 17 | "keyCredentials": null, 18 | "identifierUris": [ 19 | "" 20 | ], 21 | "groupMembershipClaims": "", 22 | "optionalClaims": null, 23 | "parentalControlSettings": null, 24 | "publisherDomain": "", 25 | "appRoles": null, 26 | "passwordCredentials": null, 27 | "signInAudience": "", 28 | "requiredResourceAccess": null 29 | } 30 | -------------------------------------------------------------------------------- /test/assets/NewGraphBetaObjectCallRecord.json: -------------------------------------------------------------------------------- 1 | { 2 | "lastModifiedDateTime": "\/Date(1577865600000)\/", 3 | "participants": null, 4 | "startDateTime": "\/Date(1577865600000)\/", 5 | "type": "unknown", 6 | "version": 0, 7 | "organizer": null, 8 | "endDateTime": "\/Date(1577865600000)\/", 9 | "modalities": null, 10 | "joinWebUrl": "" 11 | } 12 | -------------------------------------------------------------------------------- /test/assets/NewGraphObjectBetaCallRecord.json: -------------------------------------------------------------------------------- 1 | { 2 | "lastModifiedDateTime": "\/Date(1577865600000)\/", 3 | "participants": null, 4 | "startDateTime": "\/Date(1577865600000)\/", 5 | "type": "unknown", 6 | "id": "", 7 | "version": 0, 8 | "organizer": null, 9 | "endDateTime": "\/Date(1577865600000)\/", 10 | "modalities": null, 11 | "joinWebUrl": "" 12 | } 13 | -------------------------------------------------------------------------------- /test/common/GetParameterTestFunction.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | function GetParameterTestFunction($commandName) { 16 | $command = get-command $commandName 17 | $block = $command.ScriptBlock 18 | $defaultParameterSet = $command.parametersets | where isdefault -eq $true 19 | 20 | $paramBlockString = $block.ast.body.paramblock.tostring() 21 | 22 | $blockBodyString = { 23 | [PSCustomObject] @{BoundParameters=$PSBoundParameters;ParameterSetName=$PSCmdlet.ParameterSetName} 24 | }.tostring() 25 | 26 | $defaultParameterSetName = if ( $defaultParameterSet ) { 27 | ", defaultparametersetname = '$($defaultparameterset.name)'" 28 | } 29 | 30 | $testFunctionString = @" 31 | [cmdletbinding(positionalbinding=`$false $defaultParameterSetName)] 32 | $paramBlockString 33 | $blockBodyString 34 | "@ 35 | 36 | [ScriptBlock]::Create($testFunctionString) 37 | } 38 | -------------------------------------------------------------------------------- /test/common/NoninteractiveSubtestHelper.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2020, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | $shell = if ( $PSEdition -eq 'Desktop' ) { 16 | 'powershell' 17 | } else { 18 | 'pwsh' 19 | } 20 | 21 | $parameterSetTestsFinished = $null 22 | 23 | 24 | # This strange behavior is required because these tests try to trigger cases where mandatory parameters are not specified, 25 | # which would normally hange the test. To avoid this, we relaunch just these tests in a non-interactive powershell. 26 | # As long as all tests in this new powershell succeed, we record all of the tests in the instance that launched the 27 | # sub-instance as successful. If one or more tests fails in the sub-instance, all those tests in this instance 28 | # are marked as failed. 29 | # TODO: get the detailed status from the instance and mark it only those tests that failed. 30 | if ( ! ( get-variable ThisTestStarted -erroraction ignore ) ) { 31 | $command = "`$global:ThisTestStarted = `$true; invoke-pester -enableexit -tag parameterbinding -script '$($myinvocation.mycommand.source)'" 32 | & $shell -noninteractive -noprofile -command $command | out-host 33 | if ( $LASTEXITCODE -ne 0 ) { 34 | throw "Failed with exit code '$LASTEXITCODE'" 35 | } 36 | $parameterSetTestsFinished = $true 37 | } else { 38 | $parameterSetTestsFinished = $false 39 | } 40 | -------------------------------------------------------------------------------- /test/integration/cmdlets/Get-GraphRelatedItem.tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2023, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | if ( ! ( & $psscriptroot/../../IsIntegrationTestRun.ps1 ) ) { 16 | return 17 | } 18 | 19 | Describe "The Get-GraphRelatedItem command executing unmocked" { 20 | 21 | Set-StrictMode -Version 2 22 | $erroractionpreference = 'stop' 23 | 24 | Context "when invoked for simple use cases" { 25 | BeforeAll { 26 | $currentConnection = Connect-GraphApi -Connection $global:__IntegrationTestGraphConnection 27 | $thisApplicationId = $currentconnection.identity.app.appid 28 | $thisServicePrincipal = Get-GraphApplicationServicePrincipal $thisApplicationId 29 | 30 | $thisTestInstanceId = New-Guid | select -expandproperty guid 31 | 32 | $appTags = $global:__IntegrationTestInfo.TestRunId, $thisTestInstanceId, '__IntegrationTest__' 33 | $appPrefix = 'ggri-test-app' 34 | 35 | $apps = 0..4 | foreach { 36 | # Use SkipTenantRegistration to avoid registering a service principal. 37 | # Otherwise when we later enumerate owned objects we'll get both the app and the service principal. 38 | # For simplicity, we'd like to just have the app 39 | New-GraphApplication -Name "$appPrefix$($_)" -SkipTenantRegistration 40 | } 41 | } 42 | 43 | It "should successfully return owned objects created by this app when accessing the ownedObjects relationship of this app's service principal" { 44 | $ownedApps = Get-GraphRelatedItem /servicePrincipals/$($thisServicePrincipal.Id) -Relationship ownedObjects 45 | $testAppSubset = $ownedApps | where appId -in $apps.appId 46 | $testAppSubset | measure-object | select-object -expandproperty count | Should Be ( $apps | measure-object | select-object -expandproperty count ) 47 | $testAppSubset | get-member -membertype noteproperty | measure-object | select-object -expandproperty count | Should BeGreaterThan 10 48 | } 49 | 50 | It "should successfully return owned objects created by this app with a subset of properties when accessing the ownedObjects relationship of this app's service principal using select to project only 3 properties" { 51 | $projectedProperties = 'id', 'displayName', 'appId' 52 | $expectedProperties = $projectedProperties + '@odata.type' 53 | 54 | $ownedApps = Get-GraphRelatedItem /servicePrincipals/$($thisServicePrincipal.Id) -Relationship ownedObjects -Select id, displayName, appId 55 | $testAppSubset = $ownedApps | where appId -in $apps.appId 56 | 57 | $testAppSubset | measure-object | select-object -expandproperty count | Should Be ( $apps | measure-object | select-object -expandproperty count ) 58 | $testAppSubset | get-member -membertype noteproperty | select-object -expandproperty Name | compare-object $expectedProperties | Should Be $null 59 | } 60 | 61 | AfterAll { 62 | foreach ( $app in ( Get-GraphApplication -Tags $thisTestInstanceId ) ) { 63 | $app | Remove-GraphItem 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /test/integration/cmdlets/Get-GraphResourceWithMetadata.tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2023, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | if ( ! ( & $psscriptroot/../../IsIntegrationTestRun.ps1 ) ) { 16 | return 17 | } 18 | 19 | Describe "The Get-GraphResourceWithMetadata command executing unmocked" { 20 | 21 | Set-StrictMode -Version 2 22 | $erroractionpreference = 'stop' 23 | 24 | Context "when invoked for simple use cases" { 25 | BeforeAll { 26 | Connect-GraphApi -Connection $global:__IntegrationTestGraphConnection | out-null 27 | $organizationId = (get-graphconnection -current).identity.tenantdisplayid 28 | } 29 | 30 | It "should succeed when issuing a request for the organization object" { 31 | $actualOrganization = Get-GraphResourceWithMetadata /organization 32 | $actualOrganization.Id | Should Be $organizationId 33 | $actualOrganization.displayName.Length | Should BeGreaterThan 0 34 | } 35 | 36 | It "should have metadata for the output of a request for the organization object" { 37 | $graphName = Get-Graph -current | select-object -ExpandProperty Name 38 | $actualOrganization = Get-GraphResourceWithMetadata /organization 39 | $uriMetadata = $actualOrganization | Get-GraphUri 40 | $urIMetadata.OriginalString | Should Be "/$($graphName):/organization/$organizationid" 41 | } 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/integration/cmdlets/Test-Graph.tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2022, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | if ( ! ( & $psscriptroot/../../IsIntegrationTestRun.ps1 ) ) { 16 | return 17 | } 18 | 19 | Describe "The Test-Graph command executing unmocked" { 20 | 21 | Set-StrictMode -Version 2 22 | $erroractionpreference = 'stop' 23 | 24 | Context "when invoked for simple use cases" { 25 | 26 | It "should succeed when given no parameters" { 27 | { Test-Graph | out-null } | Should Not Throw 28 | } 29 | 30 | It "should succeed when given a cloud parameter" { 31 | { Test-Graph -cloud Public | out-null } | Should Not Throw 32 | { Test-Graph -cloud ChinaCloud | out-null } | Should Not Throw 33 | { Test-Graph -cloud USGovernmentCloud | out-null } | Should Not Throw 34 | } 35 | 36 | It "should succeed when given a custom graph URI parameter" { 37 | { Test-Graph -endpointuri 'https://graph.microsoft.com' | out-null } | Should Not Throw 38 | } 39 | 40 | It "should succeed when given a verbose parameter" { 41 | { Test-Graph -verbose *> $null } | Should Not Throw 42 | } 43 | 44 | It "should return a result with expected members" { 45 | $testResult = Test-Graph 46 | $testResult.NonfatalStatus | Should Not Be 0 47 | [Math]::Abs( ( [DateTimeOffset]::now - $testResult.ServerTimestamp ).Ticks ) | Should BeLessThan 6000000000 # 10 minutes 48 | ($testResult.ClientRequestTimestamp.GetType()) | Should BeExactly ([DateTimeOffset]) 49 | ($testResult.ClientResponseTimestamp.GetType()) | Should BeExactly ([DateTimeOffset]) 50 | { [guid] $testResult.RequestId } | Should Not Throw 51 | $testResult.ClientElapsedTime | Should BeLessThan ([TimeSpan]::new(0,2,0)) 52 | $testResult.Slice | Should Not Be NullorEmpty 53 | $testResult.Ring | Should Not Be NullOrEmpty 54 | $testResult.ScaleUnit | Should Not Be NullOrEmpty 55 | $testResult.TestUri | Should Be 'https://graph.microsoft.com/v1.0/$metadata' 56 | $testResult.DataCenter | Should Not Be NullOrEmpty 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/integration/pipeline/GraphResponseObject.tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright 2023, Adam Edwards 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | if ( ! ( & $psscriptroot/../../IsIntegrationTestRun.ps1 ) ) { 16 | return 17 | } 18 | 19 | Describe "The GraphResponseObject type" { 20 | 21 | Set-StrictMode -Version 2 22 | 23 | Context "When the pipeline processes a GraphResponseObject type" { 24 | BeforeAll { 25 | Connect-GraphApi -Connection $global:__IntegrationTestGraphConnection | out-null 26 | $thisTestInstanceId = New-Guid | select -expandproperty guid 27 | 28 | $appTags = $global:__IntegrationTestInfo.TestRunId, $thisTestInstanceId, '__IntegrationTest__' 29 | } 30 | 31 | It "should successfully pipe a GraphResponseObject from New-GraphApplication to Remove-GraphItem" { 32 | $testAppName = 'SimpleTestAppToDelete' + $thisTestInstanceId 33 | $newApp = New-GraphApplication -Name $testAppName -Tags $appTags 34 | $newApp.DisplayName | Should Be $testAppName 35 | Get-GraphApplication $newApp.AppId | Should Not Be $null 36 | { $newApp | Remove-GraphItem } | Should Not Throw 37 | 38 | # Use SilentlyContinue below due to issues with Ignore on Windows PowerShell (not Core) 39 | Get-GraphApplication $newApp.AppId -erroraction silentlycontinue | Should Be $null 40 | } 41 | 42 | AfterAll { 43 | Get-GraphApplication -Tags $thisTestInstanceId | Remove-GraphItem 44 | } 45 | } 46 | } 47 | --------------------------------------------------------------------------------